コミットを比較

..

7 コミット

作成者 SHA1 メッセージ 日付
みてるぞ f290e64a4e feat: 旧検索ページから最新取得(暫定)(#2) (#6)
#2

Co-authored-by: miteruzo <miteruzo@naver.com>
Reviewed-on: #6
2026-01-09 04:15:51 +09:00
みてるぞ 76e41ad78d feat: 例外出力(#3) (#5)
#3

Co-authored-by: miteruzo <miteruzo@naver.com>
Reviewed-on: #5
2026-01-09 03:48:50 +09:00
みてるぞ 85670982f0 gitignore 追加 2025-10-23 00:03:00 +09:00
みてるぞ 32ecf2d00f #1 2025-08-17 03:08:45 +09:00
みてるぞ 9cadadaae3 #1 2025-08-17 00:52:16 +09:00
みてるぞ 9fec1cf5f9 #1 2025-08-16 18:46:53 +09:00
みてるぞ cf6391d15c #1 2025-08-16 18:45:03 +09:00
3個のファイルの変更119行の追加80行の削除
+1
ファイルの表示
@@ -0,0 +1 @@
__pycache__
+8 -1
ファイルの表示
@@ -1 +1,8 @@
from .module import VideoInfo, fetch_comments, fetch_embed_info, fetch_video_info """
ニコニコ動画から情報取得するためのユーティリティ
"""
from .module import (VideoInfo,
fetch_embed_info,
fetch_latest_video,
fetch_video_info)
+110 -79
ファイルの表示
@@ -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,11 +38,14 @@ 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 tags_str: str = (bs.find ('meta', attrs = { 'name': 'keywords' })
.get ('content')) # type: ignore
tags = tags_str.split (',') tags = tags_str.split (',')
description = bs.find ('meta', attrs = { 'name': 'description' }).get ('content') # type: ignore description = (bs.find ('meta', attrs = { 'name': 'description' })
except (AttributeError, TypeError): .get ('content')) # type: ignore
except (AttributeError, TypeError) as ex:
print (ex)
return None return None
return { 'contentId': video_code, return { 'contentId': video_code,
@@ -37,59 +54,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,29 +87,88 @@ 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) as ex:
pass print (ex)
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) as ex:
pass print (ex)
return (title, description, thumbnail) return (title, description, thumbnail)
def fetch_latest_video (
tags: list[str],
) -> VideoInfo | None:
"""
ニコニコから指定したタグが含まれる動画を検索し,最新のものを返す.
Parameters
----------
tags: list[str]
タグ・リスト
Return
------
VideoInfo | None
動画情報
"""
tag = ' OR '.join (tags)
url = f"https://www.nicovideo.jp/tag/{ tag }"
params = { 'new_search': 'false',
'sort': 'f',
'order': 'd' }
video_info = { }
bs = _create_bs_from_url (url, params)
if bs is None:
return None
try:
video = (bs.find_all ('ul', class_ = 'videoListInner')[1]
.find ('li', class_ = 'item'))
video_info['contentId'] = video['data-video-id']
except (AttributeError, IndexError, KeyError, TypeError) as ex:
print (ex)
return None
return fetch_video_info (video_info['contentId'])
def _create_bs_from_url ( 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 = { }
try: try:
req = requests.get (url, params = params, timeout = 60) req = requests.get (url, params = params, timeout = 60)
except Timeout: except Timeout as ex:
print (ex)
return None return None
if req.status_code != 200: if req.status_code != 200:
@@ -137,23 +180,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