ぼざろクリーチャーシリーズ DB 兼 API(自分用)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

update_db.py 10 KiB

2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. import json
  2. import os
  3. import random
  4. import string
  5. import time
  6. from dataclasses import dataclass
  7. from datetime import datetime
  8. import mysql.connector
  9. import requests
  10. def main (
  11. ) -> None:
  12. conn = mysql.connector.connect (host = os.environ['MYSQL_HOST'],
  13. user = os.environ['MYSQL_USER'],
  14. password = os.environ['MYSQL_PASS'])
  15. video_dao = VideoDao (conn)
  16. api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ'])
  17. update_video_table (video_dao, api_data)
  18. # TODO: 書くこと
  19. def update_video_table (
  20. video_dao: VideoDao,
  21. api_data: list[dict],
  22. ) -> None:
  23. # TODO: videos 取得書くこと
  24. video_dao.upsert_all (videos)
  25. video_dao.delete_nonexistent_data (video_id_list)
  26. # TODO: 書くこと
  27. def fetch_comments (
  28. video_id: str,
  29. ) -> list:
  30. headers = { 'X-Frontend-Id': '6',
  31. 'X-Frontend-Version': '0' }
  32. action_track_id = (
  33. ''.join (random.choice (string.ascii_letters + string.digits)
  34. for _ in range (10))
  35. + '_'
  36. + str (random.randrange (10 ** 12, 10 ** 13)))
  37. url = (f"https://www.nicovideo.jp/api/watch/v3_guest/{ video_id }"
  38. + f"?actionTrackId={ action_track_id }")
  39. res = requests.post (url, headers = headers, timeout = 60).json ()
  40. try:
  41. nv_comment = res['data']['comment']['nvComment']
  42. except KeyError:
  43. return []
  44. if nv_comment is None:
  45. return []
  46. headers = { 'X-Frontend-Id': '6',
  47. 'X-Frontend-Version': '0',
  48. 'Content-Type': 'application/json' }
  49. params = { 'params': nv_comment['params'],
  50. 'additionals': { },
  51. 'threadKey': nv_comment['threadKey'] }
  52. url = nv_comment['server'] + '/v1/threads'
  53. res = (requests.post (url, json.dumps (params),
  54. headers = headers,
  55. timeout = 60)
  56. .json ())
  57. try:
  58. return res['data']['threads'][1]['comments']
  59. except (IndexError, KeyError):
  60. return []
  61. def search_nico_by_tag (
  62. tag: str,
  63. ) -> list[dict]:
  64. return search_nico_by_tags ([tag])
  65. def search_nico_by_tags (
  66. tags: list[str],
  67. ) -> list[dict]:
  68. url = ('https://snapshot.search.nicovideo.jp'
  69. + '/api/v2/snapshot/video/contents/search')
  70. # TODO: 年月日の設定ができてゐなぃのと,100 件までしか取得できなぃので何とかすること
  71. query_filter = json.dumps ({ 'type': 'or',
  72. 'filters': [
  73. { 'type': 'range',
  74. 'field': 'startTime',
  75. 'from': f"{year}-{start}T00:00:00+09:00",
  76. 'to': f"{year}-{end}T23:59:59+09:00",
  77. 'include_lower': True }] })
  78. params: dict[str, int | str]
  79. params = { 'q': ' OR '.join (tags),
  80. 'targets': 'tagsExact',
  81. '_sort': '-viewCounter',
  82. 'fields': 'contentId,title,tags,viewCounter,startTime',
  83. '_limit': 100,
  84. 'jsonFilter': query_filter }
  85. res = requests.get (url, params = params, timeout = 60).json ()
  86. return res['data']
  87. class VideoDao:
  88. def __init__ (
  89. self,
  90. conn
  91. ):
  92. self.conn = conn
  93. def fetch (
  94. self,
  95. video_id: int,
  96. with_relation_tables: bool = True,
  97. ) -> VideoDto | None:
  98. with self.conn.cursor () as c:
  99. c.execute ("""
  100. SELECT
  101. id,
  102. code,
  103. title,
  104. description,
  105. uploaded_at,
  106. deleted_at
  107. FROM
  108. videos
  109. WHERE
  110. id = %s
  111. ORDER BY
  112. id""", video_id)
  113. row = c.fetchone ()
  114. if row is None:
  115. return None
  116. return self._create_dto_from_row (row, with_relation_tables)
  117. def fetch_all (
  118. self,
  119. with_relation_tables: bool = True,
  120. ) -> list[VideoDto]:
  121. with self.conn.cursor () as c:
  122. c.execute ("""
  123. SELECT
  124. id,
  125. code,
  126. title,
  127. description,
  128. uploaded_at,
  129. deleted_at
  130. FROM
  131. videos
  132. ORDER BY
  133. id""")
  134. videos: list[VideoDto] = []
  135. for row in c.fetchall ():
  136. videos.append (self._create_dto_from_row (row, with_relation_tables))
  137. return videos
  138. def upsert_all (
  139. self,
  140. videos: list[VideoDto],
  141. with_relation_tables: bool = True,
  142. ) -> None:
  143. with self.conn.cursor () as c:
  144. for video in videos:
  145. c.execute ("""
  146. INSERT INTO
  147. videos(
  148. code,
  149. title,
  150. description,
  151. uploaded_at,
  152. deleted_at)
  153. VALUES
  154. (
  155. %s,
  156. %s,
  157. %s,
  158. %s,
  159. %s)
  160. ON DUPLICATE KEY UPDATE
  161. code = VALUES(code),
  162. title = VALUES(title),
  163. description = VALUES(description),
  164. uploaded_at = VALUES(uploaded_at),
  165. deleted_at = VALUES(deleted_at)""", (video.code,
  166. video.title,
  167. video.description,
  168. video.uploaded_at,
  169. video.deleted_at))
  170. video.id_ = c.lastrowid
  171. if with_relation_tables:
  172. VideoTagDao (self.conn).upsert_all (video.video_tags, False)
  173. CommentDao (self.conn).upsert_all (video.comments, False)
  174. VideoHistoryDao (self.conn).upsert_all (video.video_histories, False)
  175. def _create_dto_from_row (
  176. self,
  177. row,
  178. with_relation_tables: bool,
  179. ) -> VideoDto:
  180. video = VideoDto (id_ = row['id'],
  181. code = row['code'],
  182. title = row['title'],
  183. description = row['description'],
  184. uploaded_at = row['uploaded_at'],
  185. deleted_at = row['deleted_at'])
  186. if with_relation_tables:
  187. video.video_tags = VideoTagDao (self.conn).fetch_by_video_id (video.id_, False)
  188. for i in range (len (video.video_tags)):
  189. video.video_tags[i].video = video
  190. video.comments = CommentDao (self.conn).fetch_by_video_id (video.id_, False)
  191. for i in range (len (video.comments)):
  192. video.comments[i].video = video
  193. video.video_histories = VideoHistoryDao (self.conn).fetch_by_video_id (video.id_, False)
  194. for i in range (len (video.video_histories)):
  195. video.video_histories[i].video = video
  196. return video
  197. @dataclass (slots = True)
  198. class VideoDto:
  199. id_: int | None = None
  200. code: str
  201. title: str
  202. description: str
  203. uploaded_at: datetime
  204. deleted_at: datetime | None = None
  205. video_tags: list[VideoTagDto] | None = None
  206. comments: list[CommentDto] | None = None
  207. video_histories: list[VideoHistoryDto] | None = None
  208. class VideoTagDao:
  209. def __init__ (
  210. self,
  211. conn,
  212. ):
  213. self.conn = conn
  214. def fetch_by_video_id (
  215. self,
  216. video_id: int,
  217. with_relation_tables: bool = True,
  218. ) -> list[VideoTagDto]:
  219. with self.conn.cursor () as c:
  220. c.execute ("""
  221. SELECT
  222. id,
  223. video_id,
  224. tag_id,
  225. tagged_at,
  226. untagged_at
  227. FROM
  228. video_tags
  229. WHERE
  230. video_id = %s
  231. ORDER BY
  232. id""", video_id)
  233. video_tags: list[VideoTagDto] = []
  234. for row in c.fetchall ():
  235. video_tags.append (self._create_dto_from_row (row, with_relation_tables))
  236. return video_tags
  237. def _create_dto_from_row (
  238. self,
  239. row,
  240. with_relation_tables: bool,
  241. ) -> VideoTagDto:
  242. video_tag = VideoTagDto (id_ = row['id'],
  243. video_id = row['video_id'],
  244. tag_id = row['tag_id'],
  245. tagged_at = row['tagged_at'],
  246. untagged_at = row['untagged_at'])
  247. if with_relation_tables:
  248. video_tag.video = VideoDao (self.conn).fetch (video_tag.video_id, True)
  249. video_tag.tag = TagDao (self.conn).fetch (video_tag.tag_id, True)
  250. return video_tag
  251. @dataclass (slots = True)
  252. class VideoTagDto:
  253. id_: int | None = None
  254. video_id: int
  255. tag_id: int
  256. tagged_at: datetime
  257. untagged_at: datetime
  258. video: VideoDto | None = None
  259. tag: TagDto | None = None
  260. if __name__ == '__main__':
  261. main ()