docs: add maitian spider design

This commit is contained in:
Harold
2026-04-26 10:19:04 +08:00
parent 2c4eb25d2f
commit 8a64a77d91
@@ -0,0 +1,208 @@
# 麦田影院 Python 爬虫设计
## 目标
在当前 Python 仓库中新增一个符合 `base.spider.Spider` 接口的麦田影院爬虫,实现用户给出的 JS 参考行为,并遵循仓库现有的单文件 Spider 与 `unittest` 测试约定。
本次实现范围:
- 固定分类首页
- 分类分页浏览
- 详情页元信息解析
- 详情页线路分组解析
- 播放页直链解析
- 无搜索占位返回
本次不实现:
- 首页推荐抓取
- 站点筛选项
- 模糊搜索或站外搜索
- 通用 MacCMS 基类抽象
- 超出参考 JS 的额外解密分支
## 方案选择
采用“单站点直解析 + 固定分类配置 + `player_data` / `art.php` 两段式解链”的实现。
原因如下:
- 用户已经给出完整的站点行为参考,直接映射到 Python 最稳妥
- 当前仓库的同类 Spider 以单文件实现为主,维护成本最低
- 该站详情页结构与现有 `耐视点播``糯米` 接近,可以复用相同的解析思路
- 搜索明确不做,没必要为了未来复用引入新的公共抽象
## 模块边界
新增 `py/麦田影院.py`,新增 `py/tests/test_麦田影院.py`,不修改 `py/base/`
Spider 对外接口:
- `init`
- `getName`
- `homeContent`
- `homeVideoContent`
- `categoryContent`
- `searchContent`
- `detailContent`
- `playerContent`
Spider 内部 helper 负责:
- 统一请求头与绝对 URL 构造
- 文本清洗和短路径归一化
- 列表卡片解析
- 详情元信息解析
- 线路名与剧集分组解析
- `player_data` 提取与 JSON 解析
- `art.php` 两段式签名请求
## 站点配置
固定站点参数:
- `host`: `https://www.mtyy5.com`
- 固定桌面端 `User-Agent`
- 默认请求头包含 `Referer``Origin``User-Agent`
固定分类:
- `1` 电影
- `2` 电视剧
- `4` 动漫
- `3` 综艺
- `26` 短剧
- `25` 少儿
`homeContent` 返回固定分类,不抓取首页推荐。
`homeVideoContent` 固定返回 `{"list": []}`
## 分类与卡片解析
分类 URL 固定为:
- `/vodshow/{type_id}--------{page}---.html`
列表解析规则:
- 遍历 `div.public-list-box`
-`a.public-list-exp``href` 作为详情短路径
-`a.public-list-exp``title` 作为片名
-`img``data-src`,缺失时回退 `src`
-`.public-list-prb` 文本作为备注
- `vod_id` 保留短路径,例如 `/voddetail/123.html`
- `vod_pic` 输出绝对 URL
返回结构:
- `page`
- `limit`
- `total`
- `list`
不返回 `pagecount`
## 搜索策略
站点标题明确标注“无搜索”,因此 `searchContent` 不发起任何请求。
行为约束:
- 任意关键字都返回空结果
- 返回结构与仓库现有约定一致:`page``limit``total``list`
## 详情解析
详情页 URL
- `host + vod_id`
基础信息:
- `vod_id`
- `vod_name`
- `vod_pic`
- `vod_content`
- `vod_remarks`
字段来源:
- 标题优先取页面 `h1`,缺失时回退详情 `title`
- 封面优先取详情图片区 `img`
- 简介优先取剧情介绍区文本,取不到时置空
- `vod_remarks` 取详情页状态类文本,缺失允许为空
播放分组规则:
- 先读取 `a.swiper-slide` 作为线路名来源
- 线路名去除数字字符后再清洗空白
- 再按页面顺序读取 `div.anthology-list-box`
- 每个 `anthology-list-box` 对应一个线路分组
- 分组内读取 `ul.anthology-list-play > li`
- 每个剧集项取文本作为名称
- 每个剧集项取第一个子元素的 `href` 作为播放短路径
- `vod_play_from``vod_play_url` 采用仓库统一的 `$$$` / `#` 拼接规则
如果线路名数量少于分组数量,缺失部分回退为 `线路{index}`
## 播放解析
`playerContent` 只处理站内播放短路径。
播放页 URL
- `host + play_id`
解析流程:
1. 请求播放页
2. 从脚本文本中提取 `player_data=...`
3. 解析 JSON,读取 `url`
4. 如果 `url` 已经是 `http` / `https` 直链,直接返回
5. 否则先对 `url` 执行 URL 解码,再请求:
- `/static/player/art.php?get_signed_url=1&url=<decoded>`
6. 解析上一步 JSON 中的 `signed_url`
7. 再请求:
- `/static/player/art.php<signed_url>`
8. 从返回 JSON 中读取 `jmurl` 作为最终媒体地址
返回结构:
- 成功时返回 `parse=0``jx=0``playUrl=""``url=<media_url>``header=<请求头>`
- 失败时回退返回 `parse=1``jx=1``playUrl=""``url=<完整播放页地址>``header=<请求头>`
请求头约束:
- 最终播放结果保留桌面端 `User-Agent`
- `Referer` 指向播放页 URL
## 数据约束
ID 设计遵循仓库现有习惯:
- `vod_id` 使用详情短路径
- 播放项 ID 使用播放短路径
- 不把站内详情 URL 或播放 URL 预先展开写入 ID
结果约束:
- `vod_pic` 尽量输出绝对地址
- 列表、分类、搜索不返回 `pagecount`
- 搜索不触网
- 解析失败尽量返回空结果或解析页回退,而不是抛异常
## 测试策略
采用 TDD,先新增 `py/tests/test_麦田影院.py`,再实现 Spider。
测试覆盖最小闭环:
- `homeContent` 返回固定分类
- `homeVideoContent` 返回空列表
- `categoryContent` 构造正确 URL 并解析卡片
- `searchContent` 对任意关键字返回空结果且不触网
- `detailContent``swiper-slide + anthology-list-box` 组装播放分组
- `playerContent` 覆盖直链分支
- `playerContent` 覆盖 `art.php` 两段式解签分支
- `playerContent` 在脚本缺失或 JSON 无效时回退解析页