このコミットが含まれているのは:
2025-08-17 00:52:16 +09:00
コミット 9cadadaae3
2個のファイルの変更81行の追加77行の削除
+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)
+77 -76
ファイルの表示
@@ -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