py_biliys.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. # coding=utf-8
  2. # !/usr/bin/python
  3. import sys
  4. from base.spider import Spider
  5. import json
  6. import threading
  7. import requests
  8. from requests import session
  9. import time
  10. sys.path.append('..')
  11. class Spider(Spider):
  12. def getDependence(self):
  13. return ['py_bilibili']
  14. def getName(self):
  15. return "哔哩影视"
  16. def init(self, extend=""):
  17. #self.bilibili = extend[0]
  18. #print("============{0}============".format(extend))
  19. pass
  20. def isVideoFormat(self, url):
  21. pass
  22. def manualVideoCheck(self):
  23. pass
  24. # 主页
  25. def homeContent(self, filter):
  26. result = {}
  27. cateManual = {
  28. "番剧": "1",
  29. "国创": "4",
  30. "电影": "2",
  31. "电视剧": "5",
  32. "纪录片": "3",
  33. "综艺": "7",
  34. "全部": "全部",
  35. # "追番追剧": "追番追剧",
  36. "时间表": "时间表",
  37. # ————————以下可自定义关键字,结果以搜索方式展示————————
  38. # "奥特曼": "奥特曼"
  39. }
  40. classes = []
  41. for k in cateManual:
  42. classes.append({
  43. 'type_name': k,
  44. 'type_id': cateManual[k]
  45. })
  46. result['class'] = classes
  47. if filter:
  48. result['filters'] = self.config['filter']
  49. return result
  50. # 用户cookies
  51. cookies = ''
  52. userid = ''
  53. csrf = ''
  54. def getCookie(self):
  55. # self.cookies = self.bilibili.getCookie()
  56. # self.userid = self.bilibili.userid
  57. # self.csrf = self.bilibili.csrf
  58. # return self.cookies
  59. # 单用此文件,请注释掉上面4行,取消以下注释,在下方raw_cookie_line后的双引号内填写cookies/外链
  60. import http.cookies
  61. raw_cookie_line = ""
  62. if raw_cookie_line.startswith('http'):
  63. raw_cookie_line = self.fetch(raw_cookie_line).text
  64. simple_cookie = http.cookies.SimpleCookie(raw_cookie_line)
  65. cookie_jar = requests.cookies.RequestsCookieJar()
  66. cookie_jar.update(simple_cookie)
  67. rsp = session()
  68. rsp.cookies = cookie_jar
  69. content = self.fetch("https://api.bilibili.com/x/web-interface/nav", cookies=rsp.cookies)
  70. res = json.loads(content.text)
  71. if res["code"] == 0:
  72. self.cookies = rsp.cookies
  73. self.userid = res["data"].get('mid')
  74. self.csrf = rsp.cookies['bili_jct']
  75. return cookie_jar
  76. # 格式化图片,默认16:9的webp格式,降低内存占用
  77. def format_img(self, img):
  78. img += "@640w_360h_1c_100q.webp" # 格式,[jpg/png/gif]@{width}w_{high}h_{is_cut}c_{quality}q.{format}
  79. return img
  80. # 将没有page_size参数的页面分页
  81. def pagination(self, array, pg):
  82. page_size = 10 # 默认单页视频数量为10,可自定义
  83. max_number = page_size * int(pg)
  84. min_number = max_number - page_size
  85. return array[min_number:max_number]
  86. # 记录观看历史
  87. def post_history(self, aid, cid):
  88. url = 'https://api.bilibili.com/x/v2/history/report?aid={0}&cid={1}&csrf={2}'.format(aid, cid, self.csrf)
  89. requests.post(url=url, cookies=self.cookies)
  90. # 将超过10000的数字换成成以万和亿为单位
  91. def zh(self, num):
  92. if int(num) >= 100000000:
  93. p = round(float(num) / float(100000000), 1)
  94. p = str(p) + '亿'
  95. else:
  96. if int(num) >= 10000:
  97. p = round(float(num) / float(10000), 1)
  98. p = str(p) + '万'
  99. else:
  100. p = str(num)
  101. return p
  102. def homeVideoContent(self):
  103. result = {}
  104. videos = self.get_rank(1, 1)['list'][0:2]
  105. for i in [4, 2, 5, 3, 7]:
  106. videos += self.get_rank2(i, 1)['list'][0:2]
  107. result['list'] = videos
  108. return result
  109. def get_rank(self, tid, pg):
  110. result = {}
  111. url = 'https://api.bilibili.com/pgc/web/rank/list?season_type={0}&day=3'.format(tid)
  112. rsp = self.fetch(url, cookies=self.cookies)
  113. content = rsp.text
  114. jo = json.loads(content)
  115. if jo['code'] == 0:
  116. videos = []
  117. vodList = jo['result']['list']
  118. vodList = self.pagination(vodList, pg)
  119. for vod in vodList:
  120. aid = str(vod['season_id']).strip()
  121. title = vod['title'].strip()
  122. img = vod['cover'].strip()
  123. remark = vod['new_ep']['index_show']
  124. videos.append({
  125. "vod_id": 'ss' + aid,
  126. "vod_name": title,
  127. "vod_pic": self.format_img(img),
  128. "vod_remarks": remark
  129. })
  130. result['list'] = videos
  131. result['page'] = pg
  132. result['pagecount'] = 9999
  133. result['limit'] = 90
  134. result['total'] = 999999
  135. return result
  136. def get_rank2(self, tid, pg):
  137. result = {}
  138. url = 'https://api.bilibili.com/pgc/season/rank/web/list?season_type={0}&day=3'.format(tid)
  139. rsp = self.fetch(url, cookies=self.cookies)
  140. content = rsp.text
  141. jo = json.loads(content)
  142. if jo['code'] == 0:
  143. videos = []
  144. vodList = jo['data']['list']
  145. vodList = self.pagination(vodList, pg)
  146. for vod in vodList:
  147. aid = str(vod['season_id']).strip()
  148. title = vod['title'].strip()
  149. img = vod['ss_horizontal_cover'].strip()
  150. remark = vod['new_ep']['index_show']
  151. videos.append({
  152. "vod_id": aid,
  153. "vod_name": title,
  154. "vod_pic": self.format_img(img),
  155. "vod_remarks": remark
  156. })
  157. result['list'] = videos
  158. result['page'] = pg
  159. result['pagecount'] = 9999
  160. result['limit'] = 90
  161. result['total'] = 999999
  162. return result
  163. def get_zhui(self, pg, mode):
  164. result = {}
  165. if len(self.cookies) <= 0:
  166. self.getCookie()
  167. url = 'https://api.bilibili.com/x/space/bangumi/follow/list?type={2}&follow_status=0&pn={1}&ps=10&vmid={0}'.format(
  168. self.userid, pg, mode)
  169. rsp = self.fetch(url, cookies=self.cookies)
  170. content = rsp.text
  171. jo = json.loads(content)
  172. videos = []
  173. vodList = jo['data']['list']
  174. for vod in vodList:
  175. aid = str(vod['season_id']).strip()
  176. title = vod['title']
  177. img = vod['cover'].strip()
  178. remark = vod['new_ep']['index_show'].strip()
  179. videos.append({
  180. "vod_id": aid,
  181. "vod_name": title,
  182. "vod_pic": self.format_img(img),
  183. "vod_remarks": remark
  184. })
  185. result['list'] = videos
  186. result['page'] = pg
  187. result['pagecount'] = 9999
  188. result['limit'] = 90
  189. result['total'] = 999999
  190. return result
  191. def get_all(self, tid, pg, order, season_status):
  192. result = {}
  193. if len(self.cookies) <= 0:
  194. self.getCookie()
  195. url = 'https://api.bilibili.com/pgc/season/index/result?order={2}&pagesize=10&type=1&season_type={0}&page={1}&season_status={3}'.format(
  196. tid, pg, order, season_status)
  197. rsp = self.fetch(url, cookies=self.cookies)
  198. content = rsp.text
  199. jo = json.loads(content)
  200. videos = []
  201. vodList = jo['data']['list']
  202. for vod in vodList:
  203. aid = str(vod['season_id']).strip()
  204. title = vod['title']
  205. img = vod['cover'].strip()
  206. remark = vod['index_show'].strip()
  207. videos.append({
  208. "vod_id": aid,
  209. "vod_name": title,
  210. "vod_pic": self.format_img(img),
  211. "vod_remarks": remark
  212. })
  213. result['list'] = videos
  214. result['page'] = pg
  215. result['pagecount'] = 9999
  216. result['limit'] = 90
  217. result['total'] = 999999
  218. return result
  219. def get_timeline(self, tid, pg):
  220. result = {}
  221. url = 'https://api.bilibili.com/pgc/web/timeline/v2?season_type={0}&day_before=2&day_after=4'.format(tid)
  222. rsp = self.fetch(url, cookies=self.cookies)
  223. content = rsp.text
  224. jo = json.loads(content)
  225. if jo['code'] == 0:
  226. videos1 = []
  227. vodList = jo['result']['latest']
  228. for vod in vodList:
  229. aid = str(vod['season_id']).strip()
  230. title = vod['title'].strip()
  231. img = vod['cover'].strip()
  232. remark = vod['pub_index'] + ' ' + vod['follows'].replace('系列', '')
  233. videos1.append({
  234. "vod_id": aid,
  235. "vod_name": title,
  236. "vod_pic": self.format_img(img),
  237. "vod_remarks": remark
  238. })
  239. videos2 = []
  240. for i in range(0, 7):
  241. vodList = jo['result']['timeline'][i]['episodes']
  242. for vod in vodList:
  243. if str(vod['published']) == "0":
  244. aid = str(vod['season_id']).strip()
  245. title = str(vod['title']).strip()
  246. img = str(vod['cover']).strip()
  247. date = str(time.strftime("%m-%d %H:%M", time.localtime(vod['pub_ts'])))
  248. remark = date + " " + vod['pub_index']
  249. videos2.append({
  250. "vod_id": aid,
  251. "vod_name": title,
  252. "vod_pic": self.format_img(img),
  253. "vod_remarks": remark
  254. })
  255. result['list'] = self.pagination(videos2 + videos1, pg)
  256. result['page'] = pg
  257. result['pagecount'] = 9999
  258. result['limit'] = 90
  259. result['total'] = 999999
  260. return result
  261. def categoryContent(self, tid, pg, filter, extend):
  262. if len(self.cookies) <= 0:
  263. self.getCookie()
  264. if tid == "1":
  265. return self.get_rank(tid=tid, pg=pg)
  266. elif tid in {"2", "3", "4", "5", "7"}:
  267. return self.get_rank2(tid=tid, pg=pg)
  268. elif tid == "全部":
  269. tid = '1'
  270. order = '2'
  271. season_status = '-1'
  272. if 'tid' in extend:
  273. tid = extend['tid']
  274. if 'order' in extend:
  275. order = extend['order']
  276. if 'season_status' in extend:
  277. season_status = extend['season_status']
  278. return self.get_all(tid, pg, order, season_status)
  279. elif tid == "追番追剧":
  280. mode = '1'
  281. if 'mode' in extend:
  282. mode = extend['mode']
  283. return self.get_zhui(pg, mode)
  284. elif tid == "时间表":
  285. tid = '1'
  286. if 'tid' in extend:
  287. tid = extend['tid']
  288. return self.get_timeline(tid, pg)
  289. else:
  290. result = self.searchContent(key=tid, quick="false")
  291. return result
  292. def cleanSpace(self, str):
  293. return str.replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')
  294. con = threading.Condition()
  295. def get_season(self, n, nList, fromList, urlList, season_id, season_title):
  296. url = 'https://api.bilibili.com/pgc/view/web/season?season_id={0}'.format(season_id)
  297. try:
  298. rsp = self.fetch(url, headers=self.header, cookies=self.cookies)
  299. season = json.loads(rsp.text)
  300. except:
  301. with self.con:
  302. nList.remove(n)
  303. self.con.notify_all()
  304. return
  305. episode = season['result']['episodes']
  306. if len(episode) == 0:
  307. with self.con:
  308. nList.remove(n)
  309. self.con.notify_all()
  310. return
  311. playUrl = ''
  312. for tmpJo in episode:
  313. aid = tmpJo['aid']
  314. cid = tmpJo['cid']
  315. part = tmpJo['title'].replace("#", "﹟").replace("$", "﹩")
  316. if tmpJo['badge'] != '':
  317. part += ' 【' + tmpJo['badge'] + '】'
  318. part += tmpJo['long_title'].replace("#", "﹟").replace("$", "﹩")
  319. playUrl += '{0}${1}_{2}_bangumi#'.format(part, aid, cid)
  320. with self.con:
  321. while True:
  322. if n == nList[0]:
  323. fromList.append(season_title)
  324. urlList.append(playUrl)
  325. nList.remove(n)
  326. self.con.notify_all()
  327. break
  328. else:
  329. self.con.wait()
  330. def detailContent(self, array):
  331. aid = array[0]
  332. if 'ep' in aid:
  333. aid = 'ep_id=' + aid.replace('ep', '')
  334. elif 'ss' in aid:
  335. aid = 'season_id=' + aid.replace('ss', '')
  336. else:
  337. aid = 'season_id=' + aid
  338. url = "https://api.bilibili.com/pgc/view/web/season?{0}".format(aid)
  339. rsp = self.fetch(url, headers=self.header, cookies=self.cookies)
  340. jRoot = json.loads(rsp.text)
  341. jo = jRoot['result']
  342. id = jo['season_id']
  343. title = jo['title']
  344. s_title = jo['season_title']
  345. img = jo['cover']
  346. typeName = jo['share_sub_title']
  347. date = jo['publish']['pub_time'][0:4]
  348. dec = jo['evaluate']
  349. remark = jo['new_ep']['desc']
  350. stat = jo['stat']
  351. status = "弹幕: " + self.zh(stat['danmakus']) + " 点赞: " + self.zh(stat['likes']) + " 投币: " + self.zh(
  352. stat['coins']) + " 追番追剧: " + self.zh(stat['favorites'])
  353. if 'rating' in jo:
  354. score = "评分: " + str(jo['rating']['score']) + ' ' + jo['subtitle']
  355. else:
  356. score = "暂无评分" + ' ' + jo['subtitle']
  357. vod = {
  358. "vod_id": id,
  359. "vod_name": title,
  360. "vod_pic": self.format_img(img),
  361. "type_name": typeName,
  362. "vod_year": date,
  363. "vod_area": "bilidanmu",
  364. "vod_remarks": remark,
  365. "vod_actor": status,
  366. "vod_director": score,
  367. "vod_content": dec
  368. }
  369. playUrl = ''
  370. for tmpJo in jo['episodes']:
  371. aid = tmpJo['aid']
  372. cid = tmpJo['cid']
  373. part = tmpJo['title'].replace("#", "﹟").replace("$", "﹩")
  374. if tmpJo['badge'] != '':
  375. part += '【' + tmpJo['badge'] + '】'
  376. part += tmpJo['long_title'].replace("#", "﹟").replace("$", "﹩")
  377. playUrl += '{0}${1}_{2}#'.format(part, aid, cid)
  378. fromList = []
  379. urlList = []
  380. if playUrl != '':
  381. fromList.append(s_title)
  382. urlList.append(playUrl)
  383. nList = []
  384. if len(jo['seasons']) > 1:
  385. n = 0
  386. for season in jo['seasons']:
  387. season_id = season['season_id']
  388. season_title = season['season_title']
  389. if season_id == id and len(fromList) > 0:
  390. isHere = fromList.index(s_title)
  391. fromList[isHere] = season_title
  392. continue
  393. n += 1
  394. nList.append(n)
  395. t = threading.Thread(target=self.get_season, args=(n, nList, fromList, urlList, season_id, season_title))
  396. t.start()
  397. while True:
  398. _count = threading.active_count()
  399. # 计算线程数,不出结果就调大,结果少了就调小
  400. if _count <= 2:
  401. break
  402. vod['vod_play_from'] = '$$$'.join(fromList)
  403. vod['vod_play_url'] = '$$$'.join(urlList)
  404. result = {'list': [vod]}
  405. return result
  406. def searchContent(self, key, quick):
  407. if len(self.cookies) <= 0:
  408. self.getCookie()
  409. url1 = 'https://api.bilibili.com/x/web-interface/search/type?search_type=media_bangumi&keyword={0}'.format(
  410. key) # 番剧搜索
  411. rsp1 = self.fetch(url1, cookies=self.cookies)
  412. content1 = rsp1.text
  413. jo1 = json.loads(content1)
  414. rs1 = jo1['data']
  415. url2 = 'https://api.bilibili.com/x/web-interface/search/type?search_type=media_ft&keyword={0}'.format(
  416. key) # 影视搜索
  417. rsp2 = self.fetch(url2, cookies=self.cookies)
  418. content2 = rsp2.text
  419. jo2 = json.loads(content2)
  420. rs2 = jo2['data']
  421. videos = []
  422. if rs1['numResults'] == 0:
  423. vodList = jo2['data']['result']
  424. elif rs2['numResults'] == 0:
  425. vodList = jo1['data']['result']
  426. else:
  427. vodList = jo1['data']['result'] + jo2['data']['result']
  428. for vod in vodList:
  429. aid = str(vod['season_id']).strip()
  430. title = key + '➢' + vod['title'].strip().replace("<em class=\"keyword\">", "").replace("</em>", "")
  431. img = vod['cover'].strip()
  432. remark = vod['index_show']
  433. videos.append({
  434. "vod_id": aid,
  435. "vod_name": title,
  436. "vod_pic": self.format_img(img),
  437. "vod_remarks": remark
  438. })
  439. result = {
  440. 'list': videos
  441. }
  442. return result
  443. def playerContent(self, flag, id, vipFlags):
  444. if len(self.cookies) <= 0:
  445. self.getCookie()
  446. result = {}
  447. url = ''
  448. ids = id.split("_")
  449. if len(ids) < 2:
  450. return result
  451. elif len(ids) >= 2:
  452. aid = ids[0]
  453. cid = ids[1]
  454. url = 'https://api.bilibili.com/pgc/player/web/playurl?aid={0}&cid={1}&qn=116'.format(aid, cid)
  455. self.post_history(aid, cid) # 回传播放历史记录
  456. rsp = self.fetch(url, cookies=self.cookies, headers=self.header)
  457. jRoot = json.loads(rsp.text)
  458. if jRoot['code'] != 0:
  459. return {}
  460. jo = jRoot['result']
  461. ja = jo['durl']
  462. maxSize = -1
  463. position = -1
  464. for i in range(len(ja)):
  465. tmpJo = ja[i]
  466. if maxSize < int(tmpJo['size']):
  467. maxSize = int(tmpJo['size'])
  468. position = i
  469. if len(ja) > 0:
  470. if position == -1:
  471. position = 0
  472. url = ja[position]['url']
  473. result["parse"] = 0
  474. result["playUrl"] = ''
  475. result["url"] = url
  476. result["header"] = {
  477. "Referer": "https://www.bilibili.com",
  478. "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
  479. }
  480. result["contentType"] = 'video/x-flv'
  481. return result
  482. config = {
  483. "player": {},
  484. "filter": {
  485. "全部": [{"key": "tid", "name": "分类",
  486. "value": [{"n": "番剧", "v": "1"}, {"n": "国创", "v": "4"}, {"n": "电影", "v": "2"},
  487. {"n": "电视剧", "v": "5"}, {"n": "记录片", "v": "3"}, {"n": "综艺", "v": "7"}]},
  488. {"key": "order", "name": "排序",
  489. "value": [{"n": "播放数量", "v": "2"}, {"n": "更新时间", "v": "0"}, {"n": "最高评分", "v": "4"},
  490. {"n": "弹幕数量", "v": "1"}, {"n": "追看人数", "v": "3"}, {"n": "开播时间", "v": "5"},
  491. {"n": "上映时间", "v": "6"}, ]},
  492. {"key": "season_status", "name": "付费",
  493. "value": [{"n": "全部", "v": "-1"}, {"n": "免费", "v": "1"},
  494. {"n": "付费", "v": "2%2C6"}, {"n": "大会员", "v": "4%2C6"}, ]}, ],
  495. "追番追剧": [{"key": "mode", "name": "分类",
  496. "value": [{"n": "追番", "v": "1"}, {"n": "追剧", "v": "2"}, ]}, ],
  497. "时间表": [{"key": "tid", "name": "分类",
  498. "value": [{"n": "番剧", "v": "1"}, {"n": "国创", "v": "4"}, ]}, ],
  499. }
  500. }
  501. header = {
  502. "Referer": "https://www.bilibili.com",
  503. "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
  504. }
  505. def localProxy(self, param):
  506. return [200, "video/MP2T", action, ""]