docs: add feikuai spider design

This commit is contained in:
Harold
2026-04-26 08:01:44 +08:00
parent a16ee6d796
commit 49d5303b7c
@@ -0,0 +1,226 @@
# 飞快TV Python 爬虫设计
## 目标
在当前 Python 仓库中新增一个符合 `base.spider.Spider` 接口的飞快TV爬虫,行为参考用户提供的 JS 版本,但实现形式遵循仓库现有的单文件 Spider 与 `unittest` 测试约定。
本次实现范围:
- 首页分类
- 分类浏览
- 搜索
- 详情解析
- 站内在线播放分组
- 网盘分组
- 播放页 `player_aaaa` 直链解析
本次不实现:
- 首页推荐抓取
- 复杂筛选项
- 额外加密算法扩展
- 浏览器回退或动态渲染
## 方案选择
采用“固定站点 + DOM 直解析 + 播放页脚本解链 + 网盘直出”的方案,而不是为该站额外设计通用抽象或复杂 `play_id` 编码。
原因如下:
- 用户给出的参考实现本身就是固定站点 `https://feikuai.tv`
- 当前仓库对同类站点优先使用站内短路径作为 `vod_id` 与播放 ID
- 该站的在线播放和网盘在详情页上是两类不同数据,分开组装最清晰
- 播放页只需要覆盖 `player_aaaa``encrypt=1/2` 分支,过度抽象没有收益
## 模块边界
新增 `py/飞快TV.py`,不修改 `py/base/`
模块对外实现:
- `init`
- `getName`
- `homeContent`
- `homeVideoContent`
- `categoryContent`
- `searchContent`
- `detailContent`
- `playerContent`
模块内部 helper 负责:
- 统一请求头
- URL 归一化与短路径提取
- 列表卡片解析
- 详情元信息解析
- 在线播放分组解析
- 网盘分组解析
- 播放页脚本解析
- Base64 解码与结果归一化
## 站点策略
固定站点参数:
- host`https://feikuai.tv`
- 固定桌面端 `User-Agent`
- 默认请求头包含 `Referer``Origin`
请求策略:
- 分类、搜索、详情、播放页都使用 GET
- 不引入可配置 `site`
- 请求失败时返回空结果或解析回退,不抛额外异常
## 分类与首页
`homeContent` 返回固定分类:
- `1` 电影
- `2` 剧集
- `3` 综艺
- `4` 动漫
`homeVideoContent` 固定返回 `{"list": []}`
分类 URL 固定为:
- `/vodshow/{type_id}--------{page}---.html`
分类页解析规则:
- 遍历 `a.module-poster-item`
- 提取详情短路径、标题、封面、备注
- `vod_id` 保留站内短路径,例如 `/voddetail/12345.html`
- `vod_pic` 统一补全为绝对 URL
返回结构遵循仓库当前约定:
- `page`
- `limit`
- `total`
- `list`
不返回 `pagecount`
## 搜索
搜索 URL 固定为:
- `/label/search_ajax.html?wd=<urlencoded keyword>&by=time&order=desc&page=<page>`
搜索页解析规则:
- 遍历 `div.module-card-item.module-item`
- 提取详情短路径、标题、封面、备注
- 去重后返回
- 关键字为空时直接返回空列表
搜索返回:
- `page`
- `limit`
- `total`
- `list`
不返回 `pagecount`
## 详情解析
详情页 URL 为:
- `https://feikuai.tv` + `vod_id`
详情页需要同时解析基础信息、在线播放分组和网盘分组。
基础元信息:
- `vod_id`
- `vod_name`
- `vod_pic`
- `vod_content`
- `vod_remarks`
在线播放分组规则:
- 线路名来自 `div.module-tab-items-box > .module-tab-item`
- 排除带 `onclick` 的节点
- 播放列表来自 `div.module-list.tab-list`
- 排除 `.module-downlist`
- 每条剧集提取显示名与播放短路径,例如 `/vodplay/123-1-1.html`
- `vod_play_from` 中站内分组名保留页面线路名
- 站内各线路之间用 `$$$` 拼接,线路内各剧集用 `#` 拼接
网盘分组规则:
- 读取 `div.module-list > .tab-content`
- 每个网盘条目优先取 `h4` 文本的 `@` 前缀作为剧集名
- 分享链接取条目内 `p` 文本
- 根据分享链接域名识别线路名:
- `pan.quark.cn` -> `quark`
- `drive.uc.cn` -> `uc`
- `alipan.com``aliyundrive.com` -> `aliyun`
- `pan.baidu.com` -> `baidu`
- 其他回退 `pan`
- 按识别后的线路名归并,追加到 `vod_play_from` / `vod_play_url`
- 网盘链接直接写入 `vod_play_url`,不经过 `playerContent`
如果站内播放和网盘都缺失,则返回空播放字段。
## 播放解析
`playerContent` 只处理站内在线播放短路径。
播放页 URL 为:
- `https://feikuai.tv` + `id`
解析规则:
- 从页面脚本中提取 `player_aaaa=...`
- 解析 JSON 后读取 `url``encrypt`
- `encrypt == "1"` 时对 `url` 执行 `unescape`
- `encrypt == "2"` 时先 Base64 解码,再执行 `unescape`
- 解出媒体地址后返回 `parse=0``jx=0`
回退规则:
- 如果脚本缺失、JSON 解析失败或未得到可用媒体地址,则返回 `parse=1``jx=1`
- 回退 URL 使用完整播放页地址,由上层决定是否继续解析
## 数据约束
ID 设计保持仓库现有风格:
- `vod_id` 使用详情短路径
- 站内播放项 ID 使用播放短路径
- 不把完整详情 URL 或播放 URL 直接写入站内 ID 字段
字段约束:
- 列表与搜索结果不返回 `pagecount`
- `vod_pic` 必须尽量输出绝对地址
- 网盘链接作为最终分享链接直出
- 站内播放链接只在 `playerContent` 中转换为媒体直链
## 测试策略
采用 TDD,实现前先写 `py/tests/test_飞快TV.py`
测试覆盖最小闭环:
- `homeContent` 返回固定分类
- `homeVideoContent` 返回空列表
- `categoryContent` 解析分类卡片并输出短 `vod_id`
- `searchContent` 解析搜索结果并处理空关键字
- `detailContent` 同时组装站内分组与网盘分组
- `playerContent` 覆盖 `encrypt=1` 明文分支
- `playerContent` 覆盖 `encrypt=2` Base64 分支
- `playerContent` 在脚本缺失或无可用地址时回退解析页
测试方法:
- 使用 `unittest`
- 使用 `unittest.mock.patch` 隔离网络请求
- 测试 HTML 与脚本夹具直接内嵌在测试文件中
- 先跑单个新增测试模块,再决定是否补跑相关模块