このコミットが含まれているのは:
+1
-1
@@ -1 +1 @@
|
||||
from .module.py import fetch_comments
|
||||
from .module import VideoInfo, fetch_comments, fetch_embed_info, fetch_video_info
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
from typing import Any, TypedDict
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from requests.exceptions import Timeout
|
||||
|
||||
|
||||
def fetch_video_info (
|
||||
video_code: str,
|
||||
) -> VideoInfo | None:
|
||||
@@ -6,20 +19,141 @@ def fetch_video_info (
|
||||
return None
|
||||
|
||||
try:
|
||||
title = bs.find ('title')
|
||||
if title is None:
|
||||
title_tag = bs.find ('title')
|
||||
if title_tag is None:
|
||||
return None
|
||||
title = '-'.join (title.text.split ('-')[:(-1)])[:(-1)]
|
||||
title = '-'.join (title_tag.text.split ('-')[:(-1)]).strip ()
|
||||
|
||||
tags_str: str = bs.find ('meta', attrs = { 'name': 'keywords' }).get ('content') # type: ignore
|
||||
tags = tags_str.split (',')
|
||||
|
||||
description = bs.find ('meta', attrs = { 'name': 'description' }).get ('content') # type: ignore
|
||||
except Exception:
|
||||
except (AttributeError, TypeError):
|
||||
return None
|
||||
|
||||
return { 'contentId': video_code,
|
||||
'title': title,
|
||||
'tags': tags,
|
||||
'description': 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 (
|
||||
url: str,
|
||||
) -> tuple[str, str, str]:
|
||||
title: str = ''
|
||||
description: str = ''
|
||||
thumbnail: str = ''
|
||||
|
||||
bs = _create_bs_from_url (url)
|
||||
if bs is None:
|
||||
return ('', '', '')
|
||||
|
||||
tmp = bs.find ('title')
|
||||
if tmp is not None:
|
||||
title = tmp.text
|
||||
|
||||
tmp = bs.find ('meta', attrs = { 'name': 'description' })
|
||||
if tmp is not None and hasattr (tmp, 'get'):
|
||||
try:
|
||||
description = str (tmp.get ('content'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
tmp = bs.find ('meta', attrs = { 'name': 'thumbnail' })
|
||||
if tmp is not None and hasattr (tmp, 'get'):
|
||||
try:
|
||||
thumbnail = str (tmp.get ('content'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return (title, description, thumbnail)
|
||||
|
||||
|
||||
def _create_bs_from_url (
|
||||
url: str,
|
||||
params: dict | None = None,
|
||||
) -> BeautifulSoup | None:
|
||||
if params is None:
|
||||
params = { }
|
||||
|
||||
try:
|
||||
req = requests.get (url, params = params, timeout = 60)
|
||||
except Timeout:
|
||||
return None
|
||||
|
||||
if req.status_code != 200:
|
||||
return None
|
||||
|
||||
req.encoding = req.apparent_encoding
|
||||
|
||||
return BeautifulSoup (req.text, 'html.parser')
|
||||
|
||||
|
||||
class VideoInfo (TypedDict):
|
||||
contentId: str
|
||||
title: str
|
||||
tags: list[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
|
||||
|
||||
新しい課題から参照
ユーザをブロックする