docs: add 两个BT spider design

This commit is contained in:
Harold
2026-04-24 18:30:48 +08:00
parent 92ac6fac8c
commit 68e6fbac40
@@ -0,0 +1,185 @@
# 两个BT Spider 设计
## 目标
在当前 Python 仓库中新增一个符合 `base.spider.Spider` 接口的 `两个BT` spider,参考用户提供的 JS 站点逻辑,实现首页、分类、搜索、详情和播放能力,目标站点为 `https://www.bttwoo.com`
## 范围
本次实现包含:
- 新增 `py/两个BT.py`
- 新增 `py/tests/test_两个BT.py`
- 固定 14 个分类
- 首页推荐、分类页与搜索页卡片解析
- 详情页元数据与单线路剧集解析
- 播放页直链提取、iframe 二次提取与失败回退
本次实现不包含:
- 修改 `py/base/` 共享基类
- 迁移 JS 版本中的 `OmniBox.log``processScraping``getScrapeMetadata``sniffVideo`
- 真实联网测试
- 站外解析器或额外第三方线路适配
## 分类与 URL
首页固定返回以下分类:
- `zgjun` 国产剧
- `meiju` 美剧
- `jpsrtv` 日韩剧
- `movie_bt_tags/xiju` 喜剧
- `movie_bt_tags/aiqing` 爱情
- `movie_bt_tags/adt` 冒险
- `movie_bt_tags/at` 动作
- `movie_bt_tags/donghua` 动画
- `movie_bt_tags/qihuan` 奇幻
- `movie_bt_tags/xuanni` 悬疑
- `movie_bt_tags/kehuan` 科幻
- `movie_bt_tags/juqing` 剧情
- `movie_bt_tags/kongbu` 恐怖
- `gf` 高分电影
`homeContent` 只返回 `class`,不返回 `filters`。参考脚本没有定义筛选项,Python 版本也不额外引入未经验证的筛选参数。
URL 规则如下:
- 首页推荐走 `/`
- 分类页默认走 `/{tid}`
- `movie_bt_tags/*` 保留原始路径
- 页码大于 1 时追加 `paged` 查询参数
- 搜索走 `/xssssearch?q=<keyword>`
- 搜索翻页追加 `p=<page>`
分类和搜索结果返回仓库当前通用字段:
- `list`
- `page`
- `limit`
- `total`
不返回 `pagecount`
## 接口映射
- `homeContent`
- 返回固定分类
- `homeVideoContent`
- 请求首页
- 复用列表卡片解析函数返回推荐列表
- `categoryContent`
- 构造分类 URL
- 解析卡片并返回分页字段
- `searchContent`
- 构造搜索 URL
- 解析卡片并做轻量相关性过滤
- `detailContent`
- 请求详情页
- 解析标题、封面、简介、导演、主演
- 解析所有 `/v_play/` 剧集链接并组装单线路播放列表
- `playerContent`
- 解码 `play_id`
- 请求播放页并优先提取站内直链
- 必要时进入 iframe 二次提取
- 提取失败时回退 `parse=1`
## 数据模型
### 列表卡片
首页推荐、分类和搜索统一复用卡片解析逻辑:
- 从包含 `/movie/<id>.html` 的链接提取详情入口
- `vod_id` 只保留数字 ID,不返回绝对 URL
- 标题按 `h3 a``h3``a[title]``.title``.name` 顺序提取
- 图片按 `data-original``data-src``src` 顺序提取
- 图片地址统一补全为绝对 URL
- 备注从评分、状态或常见角标文本提取
- 对重复卡片按 `vod_id` 去重
### 搜索精排
搜索结果在卡片解析后做一层轻量过滤,以贴近参考脚本:
- 标题直接包含关键词时保留
- 否则按去空白后的字符集合计算交集比例
- 关键词长度不超过 2 时只做直接包含判断
- 未命中过滤条件的结果丢弃
### 详情
详情页字段按“DOM 优先,兜底选择器回退”的方式提取:
- 标题优先 `h1``h2``title`
- 封面优先 `img.poster``.poster img`、首张可用图片
- 简介优先 `.intro``.description``.desc`
- 主演与导演优先标签文本,再用正则清理 `主演``导演` 前缀
播放列表统一输出为单线路:
- `vod_play_from`: `两个BT`
- `vod_play_url`: `剧集名$playId#剧集名$playId`
选集按详情页上的 `/v_play/...html` 链接提取,顺序保持页面顺序。
### 播放 ID
`play_id` 使用轻量 JSON 编码后再 base64,至少包含:
- `pid`: 站内 `/v_play/` 的核心 ID
- `sid`: 当前影片 `vod_id`
- `name`: 原始剧集名
这样可以在播放解析阶段直接恢复播放页地址,并保留回退所需上下文。
## 播放解析
播放解析优先返回站内直链,处理顺序如下:
1. 解码 `play_id`
2. 如果输入本身已是媒体直链,则直接返回 `parse=0`
3. 构造并请求 `/v_play/<pid>.html`
4. 优先从播放页 HTML 中提取显式媒体地址,例如 `.m3u8``.mp4`
5. 若存在 iframe,则继续请求 iframe 页面并重复提取
6. 若仍无法拿到直链,则回退返回播放页 URL,并设置 `parse=1`
播放器返回头至少包含:
- `User-Agent`
- `Referer`
- `Origin`
这部分只实现仓库现有能力边界内的站内解析,不迁移参考脚本对外部运行时的依赖。
## 错误处理
- HTML 请求失败时返回空字符串,不向上抛站点异常
- 首页、分类、搜索失败时返回空列表和基本分页字段
- 详情页解析失败时返回空列表
- 播放解析失败时回退 `parse=1`
- 坏的 `play_id`、空节点和无匹配正则都按空值处理,不抛未捕获异常
## 测试策略
使用 `unittest``unittest.mock`,不走真网。
至少覆盖:
- `homeContent` 返回固定 14 个分类
- 列表卡片正确提取 `vod_id`、标题、图片、备注
- `categoryContent` 正确拼接分类 URL 与页码参数
- `searchContent` 正确请求搜索 URL 并执行相关性过滤
- `detailContent` 正确提取元数据和剧集列表
- `play_id` 编码与解码保持一致
- `playerContent` 能直接返回已给定的媒体地址
- `playerContent` 能从播放页提取媒体直链
- `playerContent` 能进入 iframe 页面继续提取
- `playerContent` 在拿不到直链时回退 `parse=1`
## 验收标准
- 新增 spider 与测试文件后,可独立运行 `tests.test_两个BT`
- 首页、分类、搜索、详情在离线夹具下输出稳定
- 播放支持“直接媒体地址 -> 播放页提取 -> iframe 二次提取 -> 失败回退”四条路径
- 不修改共享基类即可完成接入