Files
tvbox_collect/jaychouqq/yingshi/py8/dyrsok.py
T
2026-06-29 03:41:14 +00:00

245 lines
11 KiB
Python

# -*- coding: utf-8 -*-
"""
电影人生 (dyrsok.com) 爬虫源 - 最终修复版
- 多线路完整提取
- 智能播放地址处理 (直链/API路径区分)
- 超级线路/王者TV蓝光: 使用固定 cookie 请求下游 API
"""
import re, json, sys, requests
from urllib.parse import urljoin, quote
from bs4 import BeautifulSoup
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
host = "https://www.dyrsok.com"
img_origin = "https://pic2.tupian.click"
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36',
'Referer': host + '/',
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9'
}
def getName(self): return "电影人生"
def init(self, extend=""):
super().init(extend)
self.session = requests.Session()
self.session.headers.update(self.headers)
# 设置固定 cookie,让超级线路等 API 认证通过
self.session.cookies.set('ip_id', '69f2142514dcfe4bbe65fc6494043702', domain='.dyrsok.com')
self.session.cookies.set('ip_id_ok', '1777472549', domain='.dyrsok.com')
self.session.cookies.set('notice_closed', 'true', domain='.dyrsok.com')
# ===== 首页 =====
def homeContent(self, filter):
classes = [
{"type_id": "dianying", "type_name": "电影"},
{"type_id": "dianshiju", "type_name": "电视剧"},
{"type_id": "zongyi", "type_name": "综艺"},
{"type_id": "dongman", "type_name": "动漫"},
{"type_id": "duanju", "type_name": "短剧"}
]
return {"class": classes, "list": self._get_home_videos()}
def homeVideoContent(self):
return {"list": self._get_home_videos()}
def _get_home_videos(self):
videos = []
try:
r = self.session.get(self.host, timeout=15)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'html.parser')
cards = soup.select('div.relative.group a[href*="/dyrscom-"]')
seen = set()
for a in cards[:24]:
href = a.get('href', '')
if href in seen: continue
seen.add(href)
if not href.startswith('http'): href = urljoin(self.host, href)
parent = a.find_parent('div', class_='relative')
title = img_src = remark = ''
if parent:
h3 = parent.select_one('h3')
if h3: title = h3.get_text(strip=True)
img = parent.select_one('img[data-src]')
if img:
img_src = img.get('data-src', '')
if img_src.startswith('/') and not img_src.startswith('//'):
img_src = self.img_origin + img_src
badge = parent.select_one('div.text-\\[10px\\].font-bold.absolute')
if badge: remark = badge.get_text(strip=True)
if title and href:
videos.append({"vod_id": href, "vod_name": title, "vod_pic": img_src, "vod_remarks": remark})
except Exception as e: print(f"首页抓取失败: {e}")
return videos
# ===== 分类列表 =====
def categoryContent(self, tid, pg, filter, extend):
pg = int(pg) if pg and str(pg).isdigit() else 1
class_param = extend.get('class', '') if extend else ''
url = f"{self.host}/{tid}.html"
if class_param: url += f"?class={quote(class_param)}"
return self._parse_list(url, pg)
def _parse_list(self, url, pg):
videos = []
try:
r = self.session.get(url, timeout=15)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'html.parser')
cards = soup.select('div.relative.group a[href*="/dyrscom-"]')
for a in cards:
href = a.get('href', '')
if not href.startswith('http'): href = urljoin(self.host, href)
parent = a.find_parent('div', class_='relative')
title = img_src = remark = ''
if parent:
h3 = parent.select_one('h3')
if h3: title = h3.get_text(strip=True)
img = parent.select_one('img[data-src]')
if img:
img_src = img.get('data-src', '')
if img_src.startswith('/') and not img_src.startswith('//'):
img_src = self.img_origin + img_src
badge = parent.select_one('div.text-\\[10px\\].font-bold.absolute')
if badge: remark = badge.get_text(strip=True)
if title and href:
videos.append({"vod_id": href, "vod_name": title, "vod_pic": img_src, "vod_remarks": remark})
except Exception as e: print(f"列表抓取失败: {e}")
return {"list": videos, "page": pg, "pagecount": pg+1 if len(videos)>=12 else pg, "limit": 24, "total": 9999}
# ===== 详情页 (核心修复) =====
def detailContent(self, ids):
vid = ids[0] if isinstance(ids, list) else ids
if not vid.startswith('http'): vid = urljoin(self.host, vid)
vod = {"vod_id": vid, "vod_name": "", "vod_pic": "", "vod_content": "",
"vod_play_from": "电影人生", "vod_play_url": ""}
try:
r = self.session.get(vid, timeout=15)
r.encoding = 'utf-8'
html = r.text
soup = BeautifulSoup(html, 'html.parser')
# 标题/封面/简介
m = re.search(r'<meta property="og:title" content="([^"]+)"', html)
if m: vod["vod_name"] = m.group(1).strip()
m = re.search(r'<meta property="og:image" content="([^"]+)"', html)
if m: vod["vod_pic"] = m.group(1).strip()
m = re.search(r'<meta property="og:description" content="([^"]+)"', html)
if m: vod["vod_content"] = m.group(1).strip()
# === 提取所有线路 ===
play_froms = []
all_line_urls = []
# 从 originTabs 提取所有线路按钮
for a in soup.select('div[id*="originTabs"] a, div[id="originTabs"] a'):
btn = a.select_one('button')
if not btn: continue
origin = btn.get('data-origin', '')
if not origin or origin in play_froms: continue
play_froms.append(origin)
# 获取该线路的播放列表页
line_url = urljoin(self.host, a.get('href', ''))
line_html = self._fetch(line_url)
if not line_html:
all_line_urls.append(f"加载失败${origin}?play_url=")
continue
# 从 dyrs_vod_list 提取该线路的集数
vod_match = re.search(r"var\s+dyrs_vod_list\s*=\s*JSON\.parse\('([^']+)'\)", line_html)
if vod_match:
try:
raw = vod_match.group(1).replace('\\u0022','"').replace('\\/','/')
eps = json.loads(raw)
urls = []
for ep in eps:
title = ep.get('title','')
ep_url = ep.get('url','')
urls.append(f"{title}${origin}?play_url={ep_url}")
all_line_urls.append("#".join(urls))
except:
all_line_urls.append("")
else:
all_line_urls.append(f"暂无数据${origin}?play_url=")
# fallback: 直接用当前页的 dyrs_vod_list
if not play_froms:
vod_match = re.search(r"var\s+dyrs_vod_list\s*=\s*JSON\.parse\('([^']+)'\)", html)
if vod_match:
try:
raw = vod_match.group(1).replace('\\u0022','"').replace('\\/','/')
eps = json.loads(raw)
urls = []
for ep in eps:
origin = ep.get('origin','默认线路')
ep_url = ep.get('url','')
title = ep.get('title','')
if origin not in play_froms:
play_froms.append(origin)
urls.append(f"{title}${origin}?play_url={ep_url}")
all_line_urls.append("#".join(urls))
except: pass
vod["vod_play_from"] = "$$$".join(play_froms) if play_froms else "电影人生"
vod["vod_play_url"] = "$$$".join(all_line_urls) if all_line_urls else f"在线播放$默认线路?play_url="
except Exception as e: print(f"详情解析失败: {e}")
return {"list": [vod]}
def _fetch(self, url):
try:
r = self.session.get(url, timeout=10)
r.encoding = 'utf-8'
return r.text
except: return ""
# ===== 搜索 =====
def searchContent(self, key, quick, pg="1"):
pg = int(pg) if pg and str(pg).isdigit() else 1
return self._parse_list(f"{self.host}/s.html?name={quote(key)}", pg)
# ===== 播放器(超级线路/王者TV蓝光 修复版)=====
def playerContent(self, flag, id, vipFlags):
play_id = id.split("$",1)[1] if "$" in id else id
if not play_id:
return {"parse":0, "url":"", "header":{}}
if 'play_url=' in play_id:
raw_url = play_id.split('play_url=',1)[1]
# 提取 url= 后面的真实ID
import re
id_match = re.search(r'url=([a-f0-9]+)', raw_url)
vid = id_match.group(1) if id_match else ''
# 线路判断
if '超级线路' in id:
final = f"https://box.dyrs.com.de/api/super?id={vid}"
elif '王者TV蓝光' in id:
final = f"https://box.dyrs.com.de/api/m3u8?id={vid}"
else:
# 普通线路原样返回
final = raw_url if raw_url.startswith('http') else f"{self.host}{raw_url}"
return {
"parse": 0,
"url": final,
"header": {
"User-Agent": self.headers['User-Agent'],
"Referer": self.host + "/"
}
}
# 兜底
return {"parse":0, "url": play_id, "header":{}}
def isVideoFormat(self, url):
return bool(url) and re.search(r'\.(m3u8|mp4)(\?|$)', url, re.I)
def manualVideoCheck(self): return False
def destroy(self):
if hasattr(self, 'session'): self.session.close()