py_quickvod.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. # -*- coding: utf-8 -*-
  2. # @Author : Doubebly
  3. # @Time : 2025/1/19 22:00
  4. import sys
  5. import requests
  6. from lxml import etree
  7. import base64
  8. import json
  9. import re
  10. from urllib import parse
  11. sys.path.append('..')
  12. from base.spider import Spider
  13. class Spider(Spider):
  14. def getName(self):
  15. return "QuickVod"
  16. def init(self, extend):
  17. self.home_url = 'https://www.quickvod.cc'
  18. self.headers = {
  19. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"}
  20. def getDependence(self):
  21. return []
  22. def isVideoFormat(self, url):
  23. pass
  24. def manualVideoCheck(self):
  25. pass
  26. def homeContent(self, filter):
  27. return {
  28. 'class': [
  29. {'type_id': '1', 'type_name': '电影'},
  30. {'type_id': '2', 'type_name': '剧集'},
  31. {'type_id': '4', 'type_name': '动漫'}
  32. ]
  33. }
  34. def homeVideoContent(self):
  35. data = self.get_data(self.home_url)
  36. return {'list': data, 'parse': 0, 'jx': 0}
  37. def categoryContent(self, cid, page, filter, ext):
  38. # if page != 1:
  39. # return {'list': [], 'parse': 0, 'jx': 0, 'msg': '第二页'}
  40. url = f'{self.home_url}/type/{cid}-{page}.html'
  41. data = self.get_data(url)
  42. return {'list': data, 'parse': 0, 'jx': 0}
  43. def detailContent(self, did):
  44. ids = did[0]
  45. video_list = []
  46. try:
  47. res = requests.get(f'{self.home_url}/video/{ids}.html', headers=self.headers)
  48. root = etree.HTML(res.text.encode('utf-8'))
  49. vod_play_from = root.xpath('//div[@class="stui-vodlist__head"]/h3/text()')[
  50. 0] # //ul[contains(@class, "abc")] [@class="tab_control play_from"]
  51. play_list = root.xpath('//ul[contains(@class, "stui-content__playlist")]/li')
  52. p_list = []
  53. for play in play_list:
  54. name = play.xpath('./a/text()')[0]
  55. url = play.xpath('./a/@href')[0]
  56. p_list.append(f'{name}${url}')
  57. video_list.append(
  58. {
  59. 'type_name': '',
  60. 'vod_id': ids,
  61. 'vod_name': '',
  62. 'vod_remarks': '',
  63. 'vod_year': '',
  64. 'vod_area': '',
  65. 'vod_actor': '',
  66. 'vod_director': '沐辰_为爱发电',
  67. 'vod_content': '',
  68. 'vod_play_from': vod_play_from,
  69. 'vod_play_url': '#'.join(p_list)
  70. }
  71. )
  72. return {"list": video_list, 'parse': 0, 'jx': 0}
  73. except requests.RequestException as e:
  74. return {'list': [], 'msg': e}
  75. # return {"list": [], "msg": "来自py_dependence的detailContent"}
  76. def searchContent(self, key, quick, page='1'):
  77. if page != '1':
  78. return {'list': [], 'parse': 0, 'jx': 0}
  79. url = f'{self.home_url}/vodsearch/-------------.html'
  80. d = {
  81. 'wd': key,
  82. 'submit': '',
  83. }
  84. h = {
  85. 'cache-control': 'no-cache',
  86. 'content-type': 'application/x-www-form-urlencoded',
  87. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  88. }
  89. data = []
  90. try:
  91. res = requests.post(url, data=d, headers=h)
  92. root = etree.HTML(res.text.encode('utf-8'))
  93. data_list = root.xpath('//div[@class="stui-vodlist__box"]/a')
  94. for i in data_list:
  95. data.append(
  96. {
  97. 'vod_id': i.xpath('./@href')[0].split('/')[-1].split('.')[0],
  98. 'vod_name': i.xpath('./@title')[0],
  99. 'vod_pic': i.xpath('./@data-original')[0],
  100. 'vod_remarks': i.xpath('./span[2]/text()')[0]
  101. }
  102. )
  103. return {'list': data, 'parse': 0, 'jx': 0}
  104. except requests.RequestException as e:
  105. print(e)
  106. return {'list': [], 'parse': 0, 'jx': 0}
  107. def playerContent(self, flag, pid, vipFlags):
  108. play_url = 'https://gitee.com/dobebly/my_img/raw/c1977fa6134aefb8e5a34dabd731a4d186c84a4d/x.mp4'
  109. try:
  110. res = requests.get(f'{self.home_url}{pid}', headers=self.headers)
  111. res.encoding = 'utf-8'
  112. urls = re.findall(r'},\"url\":\"(.*?)\",\"url_next\"', res.text)
  113. if len(urls) == 0:
  114. return {'url': play_url, 'parse': 0, 'jx': 0}
  115. d = urls[0]
  116. headers = {
  117. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  118. 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
  119. 'origin': 'https://www.quickvod.cc'
  120. }
  121. data = {
  122. 'vid': d,
  123. }
  124. response = requests.post('https://www.quickvod.cc/qvod/api.php', headers=headers, data=data)
  125. if response.status_code == 200:
  126. en_url = response.json()['data']['url']
  127. play_url = self.de_url(en_url)
  128. host = parse.urlparse(play_url).netloc
  129. h = {
  130. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  131. 'Host': host,
  132. }
  133. return {'url': play_url, "header": h, 'parse': 0, 'jx': 0}
  134. return {'url': play_url, 'parse': 0, 'jx': 0}
  135. except requests.RequestException as e:
  136. print(e)
  137. return {'url': play_url, 'parse': 0, 'jx': 0}
  138. def localProxy(self, params):
  139. pass
  140. def destroy(self):
  141. return '正在Destroy'
  142. def get_data(self, url):
  143. data = []
  144. try:
  145. res = requests.get(url, headers=self.headers, timeout=5) # 设置超时
  146. res.raise_for_status() # 检查请求是否成功
  147. c = res.text
  148. root = etree.HTML(c.encode('utf-8'))
  149. data_list = root.xpath('//div[@class="stui-vodlist__box"]/a')
  150. for i in data_list:
  151. vod_id = i.xpath('./@href')
  152. vod_name = i.xpath('./@title')
  153. vod_pic = i.xpath('./@data-original')
  154. vod_remarks = i.xpath('./span[2]/text()')
  155. if vod_id and vod_name and vod_pic and vod_remarks: # 检查是否存在
  156. data.append({
  157. 'vod_id': vod_id[0].split('/')[-1].split('.')[0],
  158. 'vod_name': vod_name[0],
  159. 'vod_pic': vod_pic[0],
  160. 'vod_remarks': vod_remarks[0]
  161. })
  162. return data
  163. except requests.RequestException as e:
  164. # 可以记录错误信息或抛出自定义异常
  165. return data
  166. except Exception as e:
  167. # 捕获其他可能的异常
  168. return data
  169. def b64decode(self, original_string):
  170. # 添加填充
  171. padding_needed = len(original_string) % 4
  172. if padding_needed:
  173. original_string += '=' * (4 - padding_needed)
  174. # 解码 Base64 字符串
  175. decoded_bytes = base64.b64decode(original_string)
  176. # 将字节转换为字符串(假设使用 UTF-8 编码)
  177. decoded_string = decoded_bytes.decode('utf-8', errors='ignore')
  178. return decoded_string
  179. def custom_str_decode(self, encoded_string):
  180. key = '098f6bcd4621d373cade4e832627b4f6'
  181. _len = len(key)
  182. code = ''
  183. de_base64 = self.b64decode(encoded_string)
  184. i = 0
  185. while True:
  186. if i >= len(de_base64):
  187. break
  188. k = i % 32
  189. code += chr(ord(de_base64[i]) ^ ord(key[k]))
  190. i += 1
  191. return self.b64decode(code)
  192. def de_url(self, d):
  193. a = self.custom_str_decode(d).split('/')
  194. a1 = a[0]
  195. a2 = a[1]
  196. a3 = a[2]
  197. a1 = json.loads(self.b64decode(a1))
  198. a2 = json.loads(self.b64decode(a2))
  199. a3 = self.b64decode(a3)
  200. s = ''
  201. b1 = a2
  202. b2 = a1
  203. d3 = list(a3)
  204. i = 0
  205. while True:
  206. if i >= len(d3):
  207. break
  208. c1 = d3[i]
  209. c2 = bool(re.match(r'^[a-zA-Z]+$', c1))
  210. c3 = True if c1 in b2 else False
  211. if c2 and c3:
  212. s += b2[b1.index(c1)]
  213. else:
  214. s += c1
  215. i += 1
  216. return s