Files
sg/py/毒蛇.py
T
2026-08-14 11:24:54 +02:00

251 lines
7.4 KiB
Python

# -*- coding: utf-8 -*-
# by @嗷呜
import re
import sys
from pyquery import PyQuery as pq
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
self.extend = extend
def getName(self):
return "毒舌影视"
def isVideoFormat(self, url):
return False
def manualVideoCheck(self):
return False
def destroy(self):
pass
host = 'https://www.xnhrsb.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; Mobile) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/114.0.0.0 Mobile Safari/537.36',
'Referer': host,
}
# 对应 js 里的 class_name / class_url
classes_config = [
("电影", "1"),
("电视剧", "2"),
("综艺", "3"),
("动漫", "4"),
("短剧", "5"),
("豆瓣", "duoban"),
]
def homeContent(self, filter):
result = {}
classes = []
vlist = []
# 静态分类
for name, tid in self.classes_config:
classes.append({
"type_name": name,
"type_id": tid
})
# 首页推荐,沿用一级规则里的 .mrb ul li
rsp = self.fetch(self.host, headers=self.headers)
data = pq(rsp.text)
# 一级: '.mrb&&ul li;.dytit&&Text;.lazy&&data-original;.hdinfo&&Text;a&&href'
vlist.extend(self.getlist(data('.mrb ul li')))
result['class'] = classes
result['list'] = vlist
return result
def homeVideoContent(self):
return {}
def categoryContent(self, tid, pg, filter, extend):
# js: url: '/dsshiyisw/fyclass--------fypage---.html'
url = f'{self.host}dsshiyisw/{tid}--------{pg}---.html'
rsp = self.fetch(url, headers=self.headers)
data = pq(rsp.text)
videos = self.getlist(data('.mrb ul li'))
result = {
'list': videos,
'page': pg,
'pagecount': 9999,
'limit': 90,
'total': 999999
}
return result
def detailContent(self, ids):
vid = ids[0]
if not vid.startswith('http'):
if vid.startswith('/'):
url = self.host.rstrip('/') + vid
else:
url = self.host.rstrip('/') + '/' + vid
else:
url = vid
rsp = self.fetch(url, headers=self.headers)
data = pq(rsp.text)
# 二级: title/img/desc/content/tabs/lists
# title: 'h1&&Text;.moviedteail_list li&&a&&Text'
name = data('h1').text()
info_list = data('.moviedteail_list li')
def li_text(idx):
return info_list.eq(idx).text() if idx < len(info_list) else ''
# 参考 desc 顺序,大致映射
# desc: '.moviedteail_list li:eq(3)&&Text;
# .moviedteail_list li:eq(2)&&Text;
# .moviedteail_list li:eq(1)&&Text;
# .moviedteail_list li:eq(6)&&Text;
# .moviedteail_list li:eq(4)&&Text'
type_name = li_text(3)
director = li_text(2)
actor = li_text(1)
remarks = li_text(6)
year_or_area = li_text(4)
pic = data('div.dyimg img').attr('src') or ''
if pic and not pic.startswith('http'):
if pic.startswith('/'):
pic = self.host.rstrip('/') + pic
else:
pic = self.host.rstrip('/') + '/' + pic
content = data('.yp_context').text()
vod = {
'vod_id': vid,
'vod_name': name,
'vod_pic': pic,
'type_name': type_name,
'vod_year': year_or_area,
'vod_area': '',
'vod_remarks': remarks,
'vod_actor': actor,
'vod_director': director,
'vod_content': content,
'vod_play_from': '',
'vod_play_url': ''
}
# tabs: '.mi_paly_box .ypxingq_t'
tabs = [i.text() for i in data('.mi_paly_box .ypxingq_t').items()]
# lists: '.paly_list_btn:eq(#id) a:gt(0)'
# 对应每个 tab 一组播放列表
play_lists = []
play_uls = list(data('.paly_list_btn').items())
for idx, ul in enumerate(play_uls):
items = []
for i, a in enumerate(ul('a').items()):
# a:gt(0) => 跳过第一个
if i == 0:
continue
title = a.text()
href = a.attr('href') or ''
if href and not href.startswith('http'):
if href.startswith('/'):
href = self.host.rstrip('/') + href
else:
href = self.host.rstrip('/') + '/' + href
items.append(f'{title}${href}')
play_lists.append('#'.join(items))
vod['vod_play_from'] = '$$$'.join(tabs)
vod['vod_play_url'] = '$$$'.join(play_lists)
return {'list': [vod]}
def searchContent(self, key, quick, pg="1"):
# js: searchUrl: '/dsshiyisc/**----------fypage---.html'
url = f'{self.host}dsshiyisc/{key}----------{pg}---.html'
rsp = self.fetch(url, headers=self.headers)
data = pq(rsp.text)
videos = self.getlist(data('.mrb ul li'))
return {
'list': videos,
'page': pg
}
def playerContent(self, flag, id, vipFlags):
"""
对应 js 里的 lazy 解析:
- 如果页面中存在 player_aaaa 且脚本里有 "url":"xxx.m3u8" 就直接取 m3u8
- 否则返回原始地址
"""
if id.startswith('http'):
play_url = id
else:
if id.startswith('/'):
play_url = self.host.rstrip('/') + id
else:
play_url = self.host.rstrip('/') + '/' + id
p = 0 # 直接返回真实地址,不再二级解析
# 如果本身就是 m3u8
if '.m3u8' in play_url:
return {'parse': p, 'url': play_url, 'header': self.headers}
rsp = self.fetch(play_url, headers=self.headers)
html = rsp.text
# 查找包含 player_aaaa 的脚本并提取 m3u8
# js 里正则: /\"url\"\\s*:\\s*\"([^\"]+\\.m3u8[^\"]*)\"/
m = re.search(r'"url"\s*:\s*"([^"]+\.m3u8[^"]*)"', html)
if m:
real = m.group(1).replace('\\/', '/')
return {'parse': p, 'url': real, 'header': self.headers}
# 兜底,返回原地址
return {'parse': p, 'url': play_url, 'header': self.headers}
def localProxy(self, param):
return None
def getlist(self, data):
"""
对应 js 一级/搜索 规则:
'.mrb&&ul li;.dytit&&Text;.lazy&&data-original;.hdinfo&&Text;a&&href'
"""
vlist = []
for j in data.items():
name = j('.dytit').text()
pic = j('.lazy').attr('data-original') or ''
remark = j('.hdinfo').text()
href = j('a').attr('href') or ''
if pic and not pic.startswith('http'):
if pic.startswith('/'):
pic = self.host.rstrip('/') + pic
else:
pic = self.host.rstrip('/') + '/' + pic
vod_id = href
vlist.append({
'vod_id': vod_id,
'vod_name': name,
'vod_pic': pic,
'vod_remarks': remark
})
return vlist