From ed77ebdea1828d245b1c53e441c554c045548064 Mon Sep 17 00:00:00 2001 From: Harold <8866033@gmail.com> Date: Mon, 20 Apr 2026 13:56:07 +0800 Subject: [PATCH] docs: add wanou aggregate spider design --- ...026-04-20-wanou-aggregate-spider-design.md | 429 ++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 py/docs/superpowers/specs/2026-04-20-wanou-aggregate-spider-design.md diff --git a/py/docs/superpowers/specs/2026-04-20-wanou-aggregate-spider-design.md b/py/docs/superpowers/specs/2026-04-20-wanou-aggregate-spider-design.md new file mode 100644 index 0000000..da92606 --- /dev/null +++ b/py/docs/superpowers/specs/2026-04-20-wanou-aggregate-spider-design.md @@ -0,0 +1,429 @@ +# 玩偶聚合 Spider 设计 + +**日期:** 2026-04-20 + +## 目标 + +在当前 `py/` Spider 仓库中新增一个“玩偶聚合”源,提供以下能力: + +- 以站点为一级分类展示聚合站。 +- 每个站点在分类页内继续使用原站分类和筛选项。 +- 搜索时并发请求多个站点,并将同片结果聚合为单条记录。 +- 聚合详情页合并多个站点的网盘分享线路。 +- `playerContent` 不展开网盘剧集,只透传分享链接。 + +明确不做: + +- 独立管理后台。 +- 监控页面。 +- 常驻定时域名巡检任务。 +- 在 Spider 内调用网盘驱动展开剧集列表。 + +## 现状与约束 + +当前仓库是单文件 Python Spider 集合,统一继承 `base/spider.py` 中的 `Spider` 基类,测试使用 `unittest` 和 `unittest.mock`。现有仓库已接受以下模式: + +- `homeContent` 返回 `class` 和可选 `filters`。 +- `categoryContent` 使用 `extend` 解析筛选参数。 +- `detailContent` 可以返回网盘分享链接组成的 `vod_play_from` / `vod_play_url`。 +- `playerContent` 可以对网盘链接做原样透传。 +- 返回列表结果时不包含 `pagecount`。 + +因此,新 Spider 必须遵守当前仓库接口和测试风格,而不是照搬原始 Node/Fastify 插件结构。 + +## 方案选择 + +采用“配置驱动的单 Spider 聚合层”方案: + +- 新增单文件 `玩偶聚合.py`。 +- 文件内部包含站点配置、通用抓取逻辑、聚合编排逻辑。 +- 对差异较大的站点保留少量站点钩子,避免把所有逻辑写成纯硬编码分支。 + +不采用“每站点一个适配器文件”的方案,原因是当前仓库以单文件 Spider 为主,这种拆分会引入额外组织成本;也不采用“全部 if/else 硬编码”的方案,原因是后续修站难度会快速上升。 + +## 模块职责 + +`玩偶聚合.py` 内部拆成三层职责: + +### 1. 站点配置层 + +维护聚合站点定义,包括: + +- 站点 ID 和中文名。 +- 域名列表,按优先顺序排列。 +- 分类 URL 模板。 +- 搜索 URL 模板。 +- 列表、搜索、详情解析用选择器或 XPath 规则。 +- 原站默认分类。 +- 可选的本地筛选配置文件映射。 +- 站点优先级。 + +### 2. 通用采集层 + +负责: + +- 请求 HTML。 +- 域名失败切换。 +- 列表卡片解析。 +- 搜索卡片解析。 +- 详情页元数据提取。 +- 网盘分享链接识别与分组。 +- 聚合 ID 编解码辅助。 + +### 3. 聚合编排层 + +负责: + +- 首页按站点输出分类和筛选结构。 +- 分类请求路由到指定站点。 +- 搜索并发拉取多站结果。 +- 基于名称和年份做结果聚合。 +- 聚合详情拉取多站详情并合并线路。 +- `playerContent` 透传网盘链接。 + +## Spider 对外行为 + +### homeContent + +首页使用“站点优先”模式: + +- `class` 中每个条目代表一个聚合站点。 +- `type_id` 使用 `site_` 格式。 +- `type_name` 使用站点中文名。 + +每个站点的 `filters` 至少包含: + +- `categoryId` 分组,对应原站分类。 + +若该站有可落地的本地筛选配置,则继续追加该站支持的: + +- `area` +- `year` +- `class` +- `by` 或 `sort` +- 其他与模板兼容的筛选字段 + +这样,使用方在 UI 上会先选站点,再选该站原始分类和筛选项。 + +### categoryContent + +`tid` 必须是 `site_`。 + +处理逻辑: + +1. 从 `tid` 提取目标站点。 +2. 从 `extend` 中读取 `categoryId`。 +3. 读取该站支持的附加筛选项。 +4. 按该站模板拼分类 URL。 +5. 请求 HTML,解析列表卡片。 +6. 返回当前页结果。 + +当 `extend` 未提供 `categoryId` 时,默认使用该站首个分类。 + +### searchContent + +搜索默认启用聚合模式: + +1. 对所有站点并发发起搜索请求。 +2. 每个站点单独设置超时,超时只影响该站。 +3. 解析得到站内搜索卡片列表。 +4. 基于名称归一化和年份辅助规则进行跨站聚合。 +5. 选择优先级最高的站点结果作为聚合主信息。 +6. 生成聚合 `vod_id` 并返回聚合后的列表。 + +聚合结果默认不再把同片不同站拆成多条返回。 + +### detailContent + +支持两类 ID: + +- 单站详情 ID。 +- 聚合详情 ID。 + +单站详情: + +- 请求对应站点详情页。 +- 提取片名、海报、年份、导演、演员、简介。 +- 提取并识别所有网盘分享链接。 +- 生成 `vod_play_from` 和 `vod_play_url`。 + +聚合详情: + +- 解码聚合 ID 中包含的站点条目。 +- 按站点优先级依次请求多个站点详情。 +- 跳过失败站点和无网盘站点。 +- 合并多个站点的网盘线路并输出。 + +详情页不展开网盘剧集,不调用外部盘驱动。 + +### playerContent + +仅负责透传常见网盘分享链接。 + +支持: + +- `pan.baidu.com` +- `pan.quark.cn` +- `drive.uc.cn` +- `alipan.com` +- `aliyundrive.com` +- `pan.xunlei.com` +- `115.com` +- `123pan.com` +- `cloud.189.cn` 或等价天翼域名 +- `yun.139.com` 或等价移动云盘域名 + +当 `flag` 或 `id` 能识别为上述网盘分享链接时,返回: + +```python +{"parse": 0, "playUrl": "", "url": ""} +``` + +无法识别时返回空 URL,不尝试站内播放页解析。 + +## 数据模型设计 + +### 单站结果 ID + +分类页中的普通条目使用: + +`site::` + +示例: + +`site:wanou:/voddetail/12345.html` + +要求: + +- 保留站点信息。 +- 保留足够重建详情 URL 的短路径。 +- 不直接存储整页 HTML 或冗长 JSON。 + +### 聚合结果 ID + +搜索聚合结果使用: + +`agg:` + +JSON 负载为条目数组,每个条目至少包含: + +- `site` +- `path` +- `name` +- `year` + +这样可以: + +- 避免把多个站点路径用裸字符串硬拼到 `vod_id` 里。 +- 在详情阶段稳定恢复参与聚合的站点列表。 +- 控制 `vod_id` 长度和结构清晰度。 + +### 播放线路 + +`vod_play_from` 使用盘类型加来源站点命名,例如: + +- `baidu#玩偶` +- `quark#木偶` +- `a123#蜡笔` + +`vod_play_url` 直接存分享链接: + +- `百度合集$https://pan.baidu.com/s/...` +- `夸克资源$https://pan.quark.cn/s/...` + +同类型多站资源允许并存,以来源站区分。 + +## 搜索聚合规则 + +### 名称归一化 + +对片名做规范化,用于跨站聚合判断: + +- 转小写。 +- 去除空白和常见标点。 +- 去除常见分辨率或来源尾巴,如 `4K`、`HDR`、`2160P`、`1080P`。 +- 去除常见站点附加标签。 + +### 聚合判定 + +优先使用以下规则: + +1. 归一化片名完全相同,可聚合。 +2. 若双方年份都存在且不相同,则不能聚合。 +3. 若年份一致或其中一方缺失年份,允许聚合。 +4. 对明显不同的片名,即使部分包含,也不强行聚合。 + +本次实现不引入复杂模糊匹配算法,避免误合并;先用保守规则建立稳定版本。 + +### 主信息来源 + +聚合组内按站点优先级排序,优先级最高的记录决定: + +- `vod_name` +- `vod_pic` +- `vod_remarks` +- `vod_year` + +同时保留来源标签供详情阶段使用。 + +## 详情聚合规则 + +### 单站详情解析 + +每个站点详情页尽量提取: + +- 标题 +- 海报 +- 年份 +- 地区 +- 类型 +- 导演 +- 演员 +- 简介 +- 网盘分享链接 + +如果该站只有网盘资源,没有站内剧集线路,也视为有效站点。 + +### 网盘识别 + +根据链接域名识别盘类型,优先以分享域名为准,而不是文案标签: + +- 百度 +- 夸克 +- UC +- 阿里 +- 迅雷 +- 115 +- 123 +- 天翼 +- 移动云盘 + +### 合并策略 + +聚合详情按站点优先级遍历多站结果: + +- 失败站点直接跳过。 +- 无网盘链接的站点直接跳过。 +- 同一站内重复链接去重。 +- 同一分享链接跨站重复时只保留一份。 +- `vod_play_from` 顺序先按盘优先级,再按站点优先级。 + +主详情信息取第一个成功站点。 + +如果所有站点都没有可用网盘链接,则返回空播放线路。 + +## 域名容错 + +不做后台监控,只做请求时故障切换。 + +每个站点维护多个候选域名: + +1. 请求时按当前顺序逐个尝试。 +2. 某个域名成功后,将其提升到列表前部。 +3. 失败时继续尝试下一个域名。 +4. 全部失败则该站本次请求记为空结果或详情失败。 + +该策略适用于: + +- 分类页。 +- 搜索页。 +- 详情页。 + +不会引入线程、定时器或额外 HTTP 管理接口。 + +## 站点筛选策略 + +优先从仓库本地配置文件加载可用筛选项;若某站缺少本地筛选配置,则仅提供: + +- 分类 `categoryId` + +分类分组必须按用户配置顺序输出,避免被 Python 字典顺序或后处理逻辑打乱。 + +对于筛选 URL,支持两种模式: + +- 站点自定义模板。 +- 通用 CMS 模板回退。 + +## 测试设计 + +为新 Spider 新增 `tests/test_玩偶聚合.py`,覆盖三层行为。 + +### 1. 纯函数与辅助方法 + +- 站点 ID 解析。 +- 单站 `vod_id` 编解码。 +- 聚合 `vod_id` 编解码。 +- 片名归一化。 +- 聚合判定规则。 +- 网盘类型识别。 + +### 2. 单站解析与 URL 构建 + +- `homeContent` 输出站点分类和筛选结构。 +- 分类 URL 根据 `extend` 正确拼接。 +- 搜索 URL 根据关键字正确构建。 +- 单站列表卡片正确解析。 +- 单站详情正确抽取网盘链接。 +- 域名切换在首域失败后能使用备用域。 + +### 3. 聚合行为 + +- 多站搜索结果合并为单条。 +- 年份冲突结果不合并。 +- 聚合详情合并多个站点线路。 +- 聚合详情对重复分享链接去重。 +- `playerContent` 透传 quark / baidu / uc / aliyun / xunlei 等常见盘链接。 + +所有测试使用内嵌 HTML 或 mock response,不访问真实网络。 + +## 文件变更计划 + +预计后续实现阶段将修改或新增: + +- 新增 `py/玩偶聚合.py` +- 新增 `py/tests/test_玩偶聚合.py` +- 如有必要,新增本地筛选配置文件,但优先复用已有 `筛选` 目录资源 + +本设计阶段只新增本 spec 文件。 + +## 风险与处理 + +### 1. 站点结构不完全一致 + +处理方式: + +- 使用站点配置驱动。 +- 仅为少量差异保留钩子函数。 + +### 2. 搜索误聚合 + +处理方式: + +- 初版采用保守聚合规则。 +- 以完全归一化片名匹配为主,年份冲突即拆开。 + +### 3. 聚合 ID 过长 + +处理方式: + +- 使用精简 JSON 负载。 +- 仅保存详情恢复必需字段。 + +### 4. 备用域名经常失效 + +处理方式: + +- 保持请求时故障切换。 +- 不引入复杂监控模块。 + +## 实施结论 + +后续实现应严格围绕以下范围展开: + +- Python 单文件聚合 Spider。 +- 站点优先首页结构。 +- 搜索默认聚合。 +- 详情合并网盘分享线路。 +- `playerContent` 只透传分享链接。 + +超出上述范围的监控后台、定时巡检、管理 API 和网盘剧集展开,均不在本次任务中。