Files
tvboxzt/py/docs/superpowers/specs/2026-04-23-butailing-spider-design.md
T

284 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 不太灵 Python 爬虫设计
## 目标
在当前 Python 仓库中新增一个符合 `base.spider.Spider` 接口的不太灵站点爬虫,直接对接 `https://web5.mukaku.com/prod/api/v1/`,覆盖以下能力:
- 首页分类
- 首页推荐
- 分类列表
- 搜索
- 详情解析
- 网盘分享链接透传播放
实现以用户提供的 JS 版本为行为参考,但最终产物遵循当前仓库的单文件 Spider 约定,不引入 JS 端的网盘驱动依赖。
## 范围
本次实现包含:
- 新增独立脚本,文件名为 `不太灵.py`
- 使用单一 API 根地址 `https://web5.mukaku.com/prod/api/v1/`
- 支持 `home/category/detail/search/player` 主链路
- 首页返回固定五个分类
- 电影和电视剧支持 API 筛选参数映射
- 详情页提取影片元数据和网盘分享资源
- 网盘资源按分享链接独立成线路
- `playerContent` 对分享链接做原样透传
- 为新增行为补齐 `unittest`
本次实现不包含:
- 参考 JS 中的磁力画质线路输出
- 网盘驱动匹配、`getVod` 预解析或 `play` 二次解析
- 提取码自动补链
- 浏览器执行、反爬绕过或多域名探活
- 修改 `base/` 公共层
## 方案选择
采用仓库现有的“单站点单文件 + 单测”方案:
- 对外保持 `Spider` 接口兼容
- 对内拆分为 API 请求、参数归一、列表项归一、分页处理、网盘类型识别、详情播放源提取几个 helper
- 保留参考 JS 的核心接口映射和过滤参数含义,但输出改为仓库可直接消费的 `vod_play_from` / `vod_play_url`
不复刻参考 JS 的驱动解析逻辑,原因是:
- 当前 Python 仓库没有对应的运行时网盘驱动注入机制
- 用户已明确要求“直接返回分享链接”
- 单测应保持纯离线,不依赖外部驱动行为
## 模块边界
新增脚本只在站点文件内部维护逻辑,不修改 `base/`
脚本内部职责拆分如下:
- `init`
- 初始化 API 地址、鉴权参数、请求头、分类和缓存
- `homeContent`
- 返回固定 `class`,并附带电影/电视剧筛选项
- `homeVideoContent`
- 调用热门接口,返回首页推荐
- `categoryContent`
- 根据分类和扩展参数请求对应 API,并归一化结果
- `searchContent`
- 请求搜索接口,做名称过滤、本地去重和分页
- `detailContent`
- 请求详情接口,提取影片元数据与网盘分享线路
- `playerContent`
- 对分享链接直接透传
- 私有辅助函数
- 构造带鉴权的 API URL
- 发送请求并解析 JSON
- 归一化列表与详情对象
- 解析扩展筛选参数
- 本地去重与分页
- 识别网盘类型并构造播放线路
## API 与请求策略
固定配置如下:
- API 根地址:`https://web5.mukaku.com/prod/api/v1/`
- `app_id``83768d9ad4`
- `identity``23734adac0301bccdcb107c4aa21f96c`
接口映射如下:
- `homeContent`
- `getVideoTypeList`
- `homeVideoContent`
- `getVideoList`,参数 `sc=3`
- `categoryContent`
- 电影、电视剧:`getVideoMovieList`
- 热门分类:`getVideoList`
- `searchContent`
- `getVideoList`
- `detailContent`
- `getVideoDetail`
请求原则:
- 统一通过 `self.fetch` 发起 GET 请求
- 超时固定为 10 秒
- 请求头包含桌面浏览器 `User-Agent`
- JSON 解析兼容普通 JSON 字符串和带 `callback(...)` 包装的响应
- 请求失败时返回空列表或空对象,不抛出未处理异常
- 不额外引入重试和持久缓存
## 分类与筛选设计
首页固定分类为:
- `1 -> 电影`
- `2 -> 电视剧`
- `3 -> 近日热门`
- `4 -> 本周热门`
- `5 -> 本月热门`
`homeContent` 返回:
- `class`
- `filters`
筛选项只对电影和电视剧提供,来源于 `getVideoTypeList`,字段沿用参考实现:
- `sc` 影视类型
- `sd` 制片地区
- `se` 上映年份
- `sf` 资源画质
- `sh` 影视标签
- `sg` 排序方式
- `iswp` 仅网盘资源
- `status` 剧集状态,仅电视剧可用
筛选值归一原则:
- `不限``0`、空字符串统一视为未设置
- 排序默认值使用 `1`
- `iswp` 只接受 `0/1`
- `ext` 兼容 JSON、URL 编码 JSON 和 Base64 JSON
## 列表、搜索与分页设计
列表项统一映射为:
- `vod_id`
- 使用上游 `doub_id`
- `vod_name`
- 使用 `title`
- `vod_pic`
- 使用 `image``epic`
- `vod_remarks`
- 优先 `ejs`,回退 `zqxd`
- `vod_year`
- 使用 `years`
- `vod_content`
- 使用 `abstract`
- `vod_actor`
- 使用 `performer`
- `vod_director`
- 使用 `director`
- `vod_area`
- 使用 `production_area`
分类处理分两类:
- 电影、电视剧
- 直接使用后端分页接口 `getVideoMovieList`
- 单页结果按 `doub_id` 去重
- 返回 `page/limit/total/list`
- 热门分类
- 因上游分页不稳定,固定抓取较大批量结果后本地去重和分页
- 返回 `page/limit/total/list`
搜索处理规则:
- 调用 `getVideoList`
- 为避免非标题命中造成噪声,额外按标题包含关键字做一次过滤
- 结果按 `doub_id` 去重
- 再做本地分页
为了符合仓库当前约定,分类和搜索结果不返回 `pagecount`
## 详情页设计
详情页输出单个视频对象,字段至少包含:
- `vod_id`
- `vod_name`
- `vod_pic`
- `vod_remarks`
- `vod_year`
- `vod_content`
- `vod_actor`
- `vod_director`
- `vod_area`
- `vod_play_from`
- `vod_play_url`
详情数据源直接使用 `getVideoDetail` 返回对象,不依赖列表缓存。
播放源策略只处理 `movies_online_seed`
- 忽略参考 JS 中的 `ecca``all_seeds` 磁力资源
- 遍历 `movies_online_seed` 的每个分享项
- 每个有效分享链接独立生成一条线路
- 线路名格式为 `驱动类型#序号`,例如 `quark#1``baidu#2`
- 如果无法识别类型,则归类为 `other#序号`
- 同一链接去重,避免详情页重复输出
`vod_play_from` 示例:
- `baidu#1$$$quark#1$$$aliyun#1`
`vod_play_url` 示例:
- `baidu#1$https://pan.baidu.com/s/xxx$$$quark#1$https://pan.quark.cn/s/yyy`
## 网盘类型识别设计
按分享链接域名识别常见网盘类型:
- `pan.quark.cn` -> `quark`
- `pan.baidu.com` -> `baidu`
- `pan.xunlei.com` -> `xunlei`
- `www.alipan.com``aliyundrive.com` -> `aliyun`
- `123865.com``123684.com``123pan.com` -> `a123`
- 其余 -> `other`
如果 API 返回的分组名本身包含可用类型,但链接域名识别不到,则回退使用分组名归一后的结果。
## 播放设计
`playerContent` 不做任何解析:
- 如果传入的是普通分享链接,直接返回 `{"parse": 0, "url": link}`
- 如果未来上层传入了 `push://` 前缀,也兼容去前缀后返回原始分享链接
本次不支持:
- 直链视频嗅探
- 网盘二次跳转
- 播放头注入
- 网盘分享码自动处理
## 错误处理设计
错误处理以“返回空结果,不中断链路”为原则:
- API 请求失败
- 列表接口返回空列表
- 详情接口返回空对象,`detailContent` 最终返回空列表
- JSON 解析失败
- 记录日志后按空结果处理
- 详情中无网盘资源
- 返回正常元信息,`vod_play_from``vod_play_url` 置空
- 搜索关键字为空
- 直接返回空列表
## 测试设计
按 TDD 补充 `py/tests/test_灵机搜盘.py`,全部使用 `unittest.mock` 隔离网络请求。
首批测试覆盖:
1. `homeContent` 返回固定分类,并只给电影、电视剧生成筛选项。
2. `homeVideoContent` 使用 `sc=3` 请求热门列表并归一化字段。
3. `categoryContent` 在电影分类下正确构造筛选参数,并且不返回 `pagecount`
4. `categoryContent` 对热门分类执行本地去重分页。
5. `searchContent` 对标题关键字做二次过滤并去重。
6. `detailContent` 能把多个网盘链接拆成独立线路并生成 `quark#1` 这类线路名。
7. `detailContent` 忽略磁力资源,只保留网盘分享链接。
8. `playerContent` 直接透传分享链接或去掉 `push://` 前缀。
9. 网盘类型识别覆盖百度、夸克、迅雷、阿里、123 盘和兜底类型。
## 实施约束
- 文件命名使用中文站点名:`不太灵.py`
- 保持 helper 粒度适中,避免把 API 参数、详情播放源和分页逻辑堆在单个大函数里
- 不修改现有 Spider 的行为
- 先写测试并验证失败,再写实现代码