docs: add dyrs spider design

This commit is contained in:
Harold
2026-04-24 17:33:58 +08:00
parent b73694cd75
commit 2aed7f5bc3
@@ -0,0 +1,172 @@
# 电影人生 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、空节点、无匹配正则保持容错
## 测试策略
使用 `unittest``unittest.mock`,不走真网。
至少覆盖:
- `homeContent` 返回 4 个分类和对应 filters
- 卡片解析正确提取 `vod_id`、标题、图片、备注
- `categoryContent` 正确拼装 URL 与查询参数
- `detailContent` 正确提取元数据并组装多线路剧集
- `searchContent` 调用搜索 URL 并执行精排
- `playerContent` 成功提取 `/api/m3u8`
- `playerContent` 能处理 302 跳转
- `playerContent` 能从主清单中继续提取 `raw=1` 子清单
- 无法提取直链时回退 `parse=1`
## 验收标准
- 新增 spider 与测试文件后,可独立运行 `tests.test_电影人生`
- 首页、分类、搜索、详情在离线夹具下输出稳定
- 播放支持“接口提取 -> 跳转 -> 主清单下钻 -> 失败回退”四条路径
- 不修改共享基类即可完成接入