Files
tvboxzt/py/docs/superpowers/specs/2026-04-24-dyrs-spider-design.md
T
2026-04-24 17:33:58 +08:00

5.4 KiB

电影人生 Spider 设计

目标

在当前 Python 仓库中新增一个符合 base.spider.Spider 接口的 电影人生 spider,参考用户提供的 JS 站点逻辑,实现首页、分类、搜索、详情和播放能力,目标站点为 https://dyrsok.com

范围

本次实现包含:

  • 新增 py/电影人生.py
  • 新增 py/tests/test_电影人生.py
  • 固定 4 个主分类与静态筛选项
  • 首页推荐、分类页与搜索页卡片解析
  • 详情页元数据与多线路剧集解析
  • 播放页 /api/m3u8 提取、重定向跟随和主清单二次解析

本次实现不包含:

  • 修改 py/base/ 共享基类
  • 接入 JS 版本中的 OmniBox.log、播放历史、媒体信息探测
  • 真实联网测试
  • 站外解析器或额外第三方线路适配

分类与筛选

固定返回以下分类:

  • dianying 电影
  • dianshiju 电视剧
  • dongman 动漫
  • zongyi 综艺

筛选结构采用静态定义,保持与参考脚本一致的语义:

  • class
    • 不同主分类下返回预设类型值
  • sort_field
    • 默认、热度、更新时间

Python 版本沿用仓库现有格式,即筛选值使用 {"n": "...", "v": "..."}

接口映射

  • homeContent
    • 返回固定分类和筛选配置
    • 可附带首页推荐列表
  • homeVideoContent
    • 请求首页
    • 复用卡片解析函数返回推荐列表
  • categoryContent
    • 构造 /{categoryId}.html,并按筛选拼接查询参数
    • 解析卡片并返回分页字段
  • detailContent
    • 请求详情页
    • 解析标题、封面、简介、年份、地区、语言、导演、主演、标签
    • 解析多线路及每条线路下的剧集
  • searchContent
    • 构造 /s.html?name=<keyword>
    • 解析卡片并执行结果精排
  • playerContent
    • 输入为详情页剧集项里编码的播放上下文
    • 请求播放页并提取 /api/m3u8?origin=...&url=...
    • 跟随 302 或 Location 跳转
    • 若主清单中存在 raw=1 子清单,则继续下钻到最终可播 m3u8
    • 失败时回退为 parse=1

数据模型

列表卡片

列表、首页推荐和搜索统一复用卡片解析逻辑:

  • a[data-url][title] 提取标题和详情链接
  • 仅保留命中 /dyrscom- 的详情链接
  • 图片优先取 data-src,其次 src
  • 备注优先取角标文本
  • 补全相对链接与相对图片地址

vod_id 保留站内短路径,不返回绝对 URL。

详情

详情页字段按“DOM 优先、正则兜底”的策略提取:

  • 标题优先 h1/h2,再回退到 title
  • 封面优先 og:image,再回退到详情主图
  • 简介优先“剧情简介”区域,回退到 description
  • 年份、地区、语言、更新时间通过标签文本或正则抽取
  • 导演、主演、标签通过各自区块中的链接文本抽取

播放线路通过详情页上的 origin 链接展开:

  • 收集 originTabs 中的线路入口
  • 每条线路单独抓取页面
  • .seqlist 中提取剧集
  • 剧集 ID 使用 JSON 串编码,至少携带:
    • title
    • origin
    • page
    • vodName
    • pic

最终输出为仓库当前通用格式:

  • vod_play_from: $$$ 拼接线路名
  • vod_play_url: $$$ 拼接每条线路的 剧集名$playId

搜索精排

搜索结果在卡片解析后再做一次轻量精排,以贴近参考脚本行为:

  • 先标准化关键词和片名
  • 去除空白、标点与部分年份噪音
  • 完全匹配优先
  • 前缀匹配次之
  • 包含匹配再次之
  • 若所有项都未命中,则回退原始顺序

播放解析

播放解析只实现站内主链,不把 JS 版本中的外围运行时逻辑迁移进来。

处理顺序如下:

  1. 解析传入的 playId
    • 若是 JSON,读取 page/title/origin/vodName/pic
    • 若不是 JSON,则当作原始播放页地址
  2. 请求播放页 HTML
  3. 用正则提取 /api/m3u8?origin=...&url=...
  4. 请求该接口并检查响应头里的 Location
    • 有跳转时以跳转后的地址为候选结果
  5. 若跳转结果是 m3u8 主清单,则读取文本
    • 若存在 /api/m3u8?id=...raw=1 行,则拼成绝对地址并作为最终结果
  6. 若任一步失败,则返回 parse=1,由外部继续解析

播放器返回头至少包含:

  • User-Agent
  • Referer
  • 必要时补 Origin

错误处理

  • HTML 请求失败时返回空字符串,不向上抛站点异常
  • 详情页解析失败时返回空列表
  • 分类、搜索失败时返回空列表和基本分页字段
  • 播放解析失败时回退到播放页 URL 或空地址,不报未处理异常
  • 所有 helper 对缺失字段、坏 JSON、空节点、无匹配正则保持容错

测试策略

使用 unittestunittest.mock,不走真网。

至少覆盖:

  • homeContent 返回 4 个分类和对应 filters
  • 卡片解析正确提取 vod_id、标题、图片、备注
  • categoryContent 正确拼装 URL 与查询参数
  • detailContent 正确提取元数据并组装多线路剧集
  • searchContent 调用搜索 URL 并执行精排
  • playerContent 成功提取 /api/m3u8
  • playerContent 能处理 302 跳转
  • playerContent 能从主清单中继续提取 raw=1 子清单
  • 无法提取直链时回退 parse=1

验收标准

  • 新增 spider 与测试文件后,可独立运行 tests.test_电影人生
  • 首页、分类、搜索、详情在离线夹具下输出稳定
  • 播放支持“接口提取 -> 跳转 -> 主清单下钻 -> 失败回退”四条路径
  • 不修改共享基类即可完成接入