このコミットが含まれているのは:
+4
-1
@@ -1,5 +1,8 @@
|
|||||||
|
"""
|
||||||
|
ニコニコ動画から情報取得するためのユーティリティ
|
||||||
|
"""
|
||||||
|
|
||||||
from .module import (VideoInfo,
|
from .module import (VideoInfo,
|
||||||
fetch_comments,
|
|
||||||
fetch_embed_info,
|
fetch_embed_info,
|
||||||
fetch_latest_video,
|
fetch_latest_video,
|
||||||
fetch_video_info)
|
fetch_video_info)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
"""
|
||||||
|
ニコニコ動画から動画情報を取得するためのヘルパ集
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
from typing import TypedDict
|
||||||
import random
|
|
||||||
import string
|
|
||||||
import time
|
|
||||||
from typing import Any, TypedDict
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
@@ -14,6 +14,20 @@ from requests.exceptions import Timeout
|
|||||||
def fetch_video_info (
|
def fetch_video_info (
|
||||||
video_code: str,
|
video_code: str,
|
||||||
) -> VideoInfo | None:
|
) -> VideoInfo | None:
|
||||||
|
"""
|
||||||
|
動画コードからタイトル、タグ、説明を取得して返す.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
video_code: str
|
||||||
|
動画コード
|
||||||
|
|
||||||
|
Return
|
||||||
|
------
|
||||||
|
VideoInfo | None
|
||||||
|
動画情報
|
||||||
|
"""
|
||||||
|
|
||||||
bs = _create_bs_from_url (f"https://www.nicovideo.jp/watch/{ video_code }")
|
bs = _create_bs_from_url (f"https://www.nicovideo.jp/watch/{ video_code }")
|
||||||
if bs is None:
|
if bs is None:
|
||||||
return None
|
return None
|
||||||
@@ -24,10 +38,12 @@ def fetch_video_info (
|
|||||||
return None
|
return None
|
||||||
title = '-'.join (title_tag.text.split ('-')[:(-1)]).strip ()
|
title = '-'.join (title_tag.text.split ('-')[:(-1)]).strip ()
|
||||||
|
|
||||||
tags_str: str = bs.find ('meta', attrs = { 'name': 'keywords' }).get ('content') # type: ignore
|
# type: ignore
|
||||||
|
tags_str: str = bs.find ('meta', attrs = { 'name': 'keywords' }).get ('content')
|
||||||
tags = tags_str.split (',')
|
tags = tags_str.split (',')
|
||||||
|
|
||||||
description = bs.find ('meta', attrs = { 'name': 'description' }).get ('content') # type: ignore
|
# type: ignore
|
||||||
|
description = bs.find ('meta', attrs = { 'name': 'description' }).get ('content')
|
||||||
except (AttributeError, TypeError):
|
except (AttributeError, TypeError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -37,59 +53,26 @@ def fetch_video_info (
|
|||||||
'description': str (description) }
|
'description': str (description) }
|
||||||
|
|
||||||
|
|
||||||
def fetch_comments (
|
|
||||||
video_code: str,
|
|
||||||
) -> list[Comment]:
|
|
||||||
time.sleep (1.2)
|
|
||||||
|
|
||||||
headers = { 'X-Frontend-Id': '6',
|
|
||||||
'X-Frontend-Version': '0' }
|
|
||||||
|
|
||||||
action_track_id = (
|
|
||||||
''.join (random.choice (string.ascii_letters + string.digits)
|
|
||||||
for _ in range (10))
|
|
||||||
+ '_'
|
|
||||||
+ str (random.randrange (10 ** 12, 10 ** 13)))
|
|
||||||
|
|
||||||
url = (f"https://www.nicovideo.jp/api/watch/v3_guest/{ video_code }"
|
|
||||||
+ f"?actionTrackId={ action_track_id }")
|
|
||||||
|
|
||||||
res = requests.post (url, headers = headers, timeout = 60).json ()
|
|
||||||
|
|
||||||
try:
|
|
||||||
nv_comment = res['data']['comment']['nvComment']
|
|
||||||
except KeyError:
|
|
||||||
return []
|
|
||||||
if nv_comment is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
headers = { 'X-Frontend-Id': '6',
|
|
||||||
'X-Frontend-Version': '0',
|
|
||||||
'Content-Type': 'application/json' }
|
|
||||||
|
|
||||||
params = { 'params': nv_comment['params'],
|
|
||||||
'additionals': { },
|
|
||||||
'threadKey': nv_comment['threadKey'] }
|
|
||||||
|
|
||||||
url = nv_comment['server'] + '/v1/threads'
|
|
||||||
|
|
||||||
res = (requests.post (url, json.dumps (params),
|
|
||||||
headers = headers,
|
|
||||||
timeout = 60)
|
|
||||||
.json ())
|
|
||||||
|
|
||||||
try:
|
|
||||||
return res['data']['threads'][1]['comments']
|
|
||||||
except (IndexError, KeyError):
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_embed_info (
|
def fetch_embed_info (
|
||||||
url: str,
|
url: str,
|
||||||
) -> tuple[str, str, str]:
|
) -> tuple[str, str, str]:
|
||||||
title: str = ''
|
"""
|
||||||
description: str = ''
|
ニコニコ動画の URL からタイトル、詳細、サムネールを取得して返す.
|
||||||
thumbnail: str = ''
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url: str
|
||||||
|
動画 URL
|
||||||
|
|
||||||
|
Return
|
||||||
|
------
|
||||||
|
tuple[str, str, str]
|
||||||
|
タイトル、詳細、サムネールからなるタプル
|
||||||
|
"""
|
||||||
|
|
||||||
|
title = ''
|
||||||
|
description = ''
|
||||||
|
thumbnail = ''
|
||||||
|
|
||||||
bs = _create_bs_from_url (url)
|
bs = _create_bs_from_url (url)
|
||||||
if bs is None:
|
if bs is None:
|
||||||
@@ -103,14 +86,14 @@ def fetch_embed_info (
|
|||||||
if tmp is not None and hasattr (tmp, 'get'):
|
if tmp is not None and hasattr (tmp, 'get'):
|
||||||
try:
|
try:
|
||||||
description = str (tmp.get ('content'))
|
description = str (tmp.get ('content'))
|
||||||
except Exception:
|
except (AttributeError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
tmp = bs.find ('meta', attrs = { 'name': 'thumbnail' })
|
tmp = bs.find ('meta', attrs = { 'name': 'thumbnail' })
|
||||||
if tmp is not None and hasattr (tmp, 'get'):
|
if tmp is not None and hasattr (tmp, 'get'):
|
||||||
try:
|
try:
|
||||||
thumbnail = str (tmp.get ('content'))
|
thumbnail = str (tmp.get ('content'))
|
||||||
except Exception:
|
except (AttributeError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return (title, description, thumbnail)
|
return (title, description, thumbnail)
|
||||||
@@ -119,6 +102,20 @@ def fetch_embed_info (
|
|||||||
def fetch_latest_video (
|
def fetch_latest_video (
|
||||||
tags: list[str],
|
tags: list[str],
|
||||||
) -> VideoInfo | None:
|
) -> VideoInfo | None:
|
||||||
|
"""
|
||||||
|
ニコニコから指定したタグが含まれる動画を検索し,最新のものを返す.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
tags: list[str]
|
||||||
|
タグ・リスト
|
||||||
|
|
||||||
|
Return
|
||||||
|
------
|
||||||
|
VideoInfo | None
|
||||||
|
動画情報
|
||||||
|
"""
|
||||||
|
|
||||||
tag = ' OR '.join (tags)
|
tag = ' OR '.join (tags)
|
||||||
url = f"https://www.nicovideo.jp/tag/{ tag }"
|
url = f"https://www.nicovideo.jp/tag/{ tag }"
|
||||||
|
|
||||||
@@ -136,7 +133,7 @@ def fetch_latest_video (
|
|||||||
.find ('li', class_ = 'item'))
|
.find ('li', class_ = 'item'))
|
||||||
|
|
||||||
video_info['contentId'] = video['data-video-id']
|
video_info['contentId'] = video['data-video-id']
|
||||||
except Exception:
|
except (AttributeError, IndexError, KeyError, TypeError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return fetch_video_info (video_info['contentId'])
|
return fetch_video_info (video_info['contentId'])
|
||||||
@@ -146,6 +143,22 @@ def _create_bs_from_url (
|
|||||||
url: str,
|
url: str,
|
||||||
params: dict | None = None,
|
params: dict | None = None,
|
||||||
) -> BeautifulSoup | None:
|
) -> BeautifulSoup | None:
|
||||||
|
"""
|
||||||
|
URL から BeautifulSoup オブゼクトを作成する.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url: str
|
||||||
|
URL
|
||||||
|
params: dict | None
|
||||||
|
パラメータ
|
||||||
|
|
||||||
|
Return
|
||||||
|
------
|
||||||
|
BeautifulSoup | None
|
||||||
|
BeautifulSoup オブゼクト
|
||||||
|
"""
|
||||||
|
|
||||||
if params is None:
|
if params is None:
|
||||||
params = { }
|
params = { }
|
||||||
|
|
||||||
@@ -163,23 +176,11 @@ def _create_bs_from_url (
|
|||||||
|
|
||||||
|
|
||||||
class VideoInfo (TypedDict):
|
class VideoInfo (TypedDict):
|
||||||
|
"""
|
||||||
|
動画情報
|
||||||
|
"""
|
||||||
|
|
||||||
contentId: str
|
contentId: str
|
||||||
title: str
|
title: str
|
||||||
tags: list[str]
|
tags: list[str]
|
||||||
description: str
|
description: str
|
||||||
|
|
||||||
|
|
||||||
class Comment (TypedDict):
|
|
||||||
id: str
|
|
||||||
no: int
|
|
||||||
vposMs: int
|
|
||||||
body: str
|
|
||||||
commands: list[str]
|
|
||||||
userId: str
|
|
||||||
isPremium: bool
|
|
||||||
score: int
|
|
||||||
postedAt: str
|
|
||||||
nicoruCount: int
|
|
||||||
nicoruId: Any
|
|
||||||
source: str
|
|
||||||
isMyPost: bool
|
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする