From 41637357a5353e35f04b30137f30786d9eaf4fc5 Mon Sep 17 00:00:00 2001 From: Harold <8866033@gmail.com> Date: Mon, 20 Apr 2026 15:25:08 +0800 Subject: [PATCH] docs: add yisou spider design --- .../specs/2026-04-20-yisou-spider-design.md | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 py/docs/superpowers/specs/2026-04-20-yisou-spider-design.md diff --git a/py/docs/superpowers/specs/2026-04-20-yisou-spider-design.md b/py/docs/superpowers/specs/2026-04-20-yisou-spider-design.md new file mode 100644 index 0000000..b521650 --- /dev/null +++ b/py/docs/superpowers/specs/2026-04-20-yisou-spider-design.md @@ -0,0 +1,314 @@ +# 奕搜 Python 爬虫设计 + +## 目标 + +在当前 Python 仓库中新增一个符合 `base.spider.Spider` 接口的奕搜站点爬虫,覆盖以下能力: + +- 首页分类 +- 分类列表 +- 搜索 +- 详情解析 +- 网盘透传播放 + +实现以用户提供的 JS 版本为行为参考,但最终产物遵循当前仓库的单文件 Spider 约定,并对齐仓库中现有网盘类规则的输出格式。 + +## 范围 + +本次实现包含: + +- 新增独立脚本,文件名为 `奕搜.py` +- 使用单一站点主域:`https://ysso.cc` +- 支持 `home/category/detail/search/player` 主链路 +- 首页返回固定分类 +- 详情页提取影片元数据和网盘分享链接 +- `playerContent` 对分享链接做原样透传 +- 为新增行为补齐 `unittest` + +本次实现不包含: + +- 参考 JS 中的 `panUrls` 返回格式复刻 +- 站内播放线路解析 +- 网盘驱动匹配、提取码自动补链或网盘二次解析 +- 浏览器执行、反爬绕过或多域名探活 +- 修改 `base/` 公共层 + +## 方案选择 + +采用仓库现有的“单站点单文件 + 单测”方案: + +- 对外保持 `Spider` 接口兼容 +- 对内拆分为 URL 组装、文本清洗、标题备注提取、列表解析、详情页元信息提取和网盘分组几个 helper +- 保留参考 JS 的核心页面解析行为,但输出改为仓库现有 Spider 可直接消费的 `vod_play_from` / `vod_play_url` + +不直接照搬参考 JS 返回 `panUrls` 的原因是: + +- 当前 Python 仓库上层消费的是标准播放线路字段 +- 仓库里已有网盘透传型 Spider 可以直接复用输出约定 +- 本次目标是把网页上的网盘资源稳定映射到现有播放器接口,而不是维持 JS 私有数据结构 + +## 模块边界 + +新增脚本只在站点文件内部维护逻辑,不修改 `base/`。 + +脚本内部职责拆分如下: + +- `init` + - 初始化主域、请求头和固定分类 +- `homeContent` + - 返回固定 `class` +- `homeVideoContent` + - 返回空列表 +- `categoryContent` + - 请求分类页并解析卡片列表 +- `searchContent` + - 请求搜索页并解析卡片列表 +- `detailContent` + - 请求详情页,提取影片元数据与网盘分享链接 +- `playerContent` + - 对网盘分享链接或 `push://` 地址做原样透传 +- 私有辅助函数 + - 补全绝对 URL + - 清洗标题文本 + - 从标题中提取备注 + - 解析列表卡片 + - 解析详情元信息 + - 识别网盘类型并构造播放分组 + +## Host 与请求策略 + +本次只实现单域: + +- `https://ysso.cc` + +请求统一通过 `self.fetch` 发起,固定请求头至少包含: + +- `User-Agent` +- `Referer` + +请求原则: + +- HTML 请求超时固定为 10 秒 +- 请求失败时返回空 HTML 或空结果,不抛出未处理异常 +- 不引入额外缓存、重试或 JS 执行环境 + +## 分类设计 + +首页分类固定为: + +- `dy -> 电影` +- `dsj -> 电视剧` +- `zy -> 综艺` +- `dm -> 动漫` +- `jlp -> 纪录片` +- `dj -> 短剧` + +`homeContent` 返回: + +- `class` + +不返回筛选配置,因为参考实现没有分类筛选结构。 + +`homeVideoContent` 直接返回: + +- `{"list": []}` + +## 列表与搜索设计 + +分类页 URL: + +- `/.html?page=` + +搜索 URL: + +- `/search.html?keyword=&page=` + +分类页和搜索页共用同一个卡片解析器,目标结构为 `.list-boxes`。 + +卡片字段提取规则: + +- `vod_id` + - 优先取 `a.text_title_p[href]` + - 如果缺失则回退 `.left_ly a[href]` + - 输出站点短路径,不暴露完整详情 URL +- `vod_name` + - 使用标题文本清理 `[]` 标签后的结果 +- `vod_pic` + - 取 `img.image_left[src]` 并补全成绝对地址 +- `vod_remarks` + - 优先从标题中的 `[]` 标签提取 + - 若无可用备注,则回退 `.list-actions span` + +标题备注提取顺序与参考 JS 保持一致: + +1. 更新集数,如 `[更12]` 转成 `更新至12集` +2. 评分,如 `[8.5分]` 转成 `评分:8.5` +3. 年份,如 `[2025]` 转成 `首播:2025` + +分页返回字段: + +- `page` +- `limit` +- `total` +- `list` + +为了符合仓库当前约定,分类和搜索结果不返回 `pagecount`。 + +## 短 ID 设计 + +为了遵循仓库里的短 ID 规则,不对外暴露完整详情页 URL。 + +列表与搜索阶段拿到的详情链接如果为站内 URL,则统一压缩为站点相对路径,例如: + +- `/resource/12345` + +`detailContent` 接收到相对路径后再补成完整详情 URL。 + +如果上层传入的是完整 URL,也兼容直接处理,但 Spider 自身产出仍以短路径为主。 + +## 详情页设计 + +详情页输出单个视频对象,字段至少包含: + +- `vod_id` +- `vod_name` +- `vod_pic` +- `vod_remarks` +- `vod_director` +- `vod_actor` +- `vod_content` +- `vod_play_from` +- `vod_play_url` + +元信息提取规则: + +- 标题 + - `h1.articl_title` + - 标题中的 `[]` 标签会先用于生成 `vod_remarks`,再从标题中剥离 +- 封面 + - `.tc-box.article-box img` 首张图 +- 导演与编剧 + - 遍历 `#info > span` + - 命中“导演”或“编剧”标签时,读取 `.attrs a` + - 去重后以 `, ` 拼接到 `vod_director` +- 主演与演员 + - 命中“主演”或“演员”标签时,读取 `.attrs a` + - 去重后以 `, ` 拼接到 `vod_actor` +- 简介 + - 遍历正文中长度明显大于标签名的段落 + - 取首个有效长文本并压缩空白 + +提取码策略: + +- 从整页文本中匹配 `提取码[::]XXXX` +- 若命中,则追加到 `vod_remarks` +- 不把提取码写回网盘链接,不做自动拼装 + +## 网盘资源设计 + +详情页只保留网盘分享资源,不保留站内播放线路。 + +网盘链接提取策略: + +- 遍历详情页中所有 `a[target="_blank"][href]` +- 仅保留 `http/https` 绝对地址 +- 按分享链接去重,避免同链接多次输出 + +网盘类型识别范围: + +- `baidu` + - `pan.baidu.com` +- `quark` + - `pan.quark.cn` +- `uc` + - `drive.uc.cn` +- `aliyun` + - `alipan.com` + - `aliyundrive.com` +- `xunlei` + - `pan.xunlei.com` + +输出策略: + +- 能识别盘类型时,按盘类型分组输出 +- 每个分组的单条资源格式为 `<盘名>$<分享链接>` +- 同一分组内多条资源用 `#` 连接 +- 多个分组之间用 `$$$` 连接 +- `vod_play_from` 与 `vod_play_url` 的分组顺序保持一致 + +盘类型排序优先级: + +1. `baidu` +2. `quark` +3. `uc` +4. `aliyun` +5. `xunlei` + +如果详情页存在外链但都不属于上述已识别网盘: + +- 使用单一兜底线路名 `奕搜` +- 对应 `vod_play_url` 为 `1$push://#2$push://...` + +如果详情页不存在任何可用外链: + +- `vod_play_from = 奕搜` +- `vod_play_url = ""` + +这样可以保持与仓库里现有网盘透传规则一致,并且明确表达“本 Spider 只提供网盘分享链接透传”。 + +## 播放设计 + +`playerContent(flag, id, vipFlags)` 不做二次解析,只负责规范化透传: + +- 如果 `id` 以 `push://` 开头 + - 去掉前缀后返回真实分享链接 +- 如果 `id` 已经是 `http/https` 分享链接 + - 直接原样返回 +- 其他情况 + - 也原样返回,不额外猜测和改写 + +返回结构: + +- `parse = 0` +- `url = <透传后的值>` + +不附加鉴权头,不做 `jx` 回退。 + +## 错误处理 + +- 分类页、搜索页请求失败时返回空列表 +- 详情页请求失败时返回 `{"list": []}` +- 单个网盘链接识别失败时跳过盘类型识别,但不影响其他链接 +- 标题、图片、导演、主演、简介等字段缺失时返回空字符串 + +## 测试设计 + +测试覆盖以下行为: + +1. `homeContent` + - 断言固定分类完整且顺序正确 +2. 列表解析 + - 断言能从 `.list-boxes` 解析短路径 `vod_id`、清洗后的标题、图片和备注 + - 断言备注提取优先级符合参考 JS +3. `categoryContent` + - 断言分类页 URL 正确 + - 断言返回字段不包含 `pagecount` +4. `searchContent` + - 断言空关键词返回空列表 + - 断言搜索 URL 包含 URL 编码后的关键词 +5. `detailContent` + - 断言能提取标题、图片、导演、主演、简介和提取码 + - 断言详情页只输出网盘透传线路 + - 断言网盘链接按盘类型分组、去重和排序 + - 断言未识别外链时回退到 `push://` 兜底线路 +6. `playerContent` + - 断言 `push://` 去壳透传 + - 断言普通网盘分享链接原样透传 + +## 验收标准 + +- `homeContent/categoryContent/detailContent/searchContent/playerContent` 均返回符合当前项目习惯的数据结构 +- 列表和搜索结果只输出站点短路径 `vod_id` +- 标题备注提取和标题清洗与参考 JS 的优先级一致 +- 详情页只输出网盘分享线路,不保留站内播放线路 +- `playerContent` 对网盘链接仅做原样透传 +- 新增 `unittest` 能稳定覆盖主要解析分支