8.7 KiB
如意资源 Python 爬虫设计
目标
在当前 Python 仓库中新增一个符合 base.spider.Spider 接口的如意资源爬虫,行为参考用户提供的 JS 版本,但范围只保留基础采集能力:
- 首页分类与筛选
- 首页推荐
- 分类浏览
- 搜索
- 详情解析
- 播放解析
实现形式遵循当前仓库的单文件 Spider 约定和离线单测约定,不引入 OmniBox 专有能力,也不修改公共基类。
范围
本次实现包含:
- 新增独立脚本
py/如意资源.py - 新增独立测试
py/tests/test_如意资源.py - 顺序尝试多个采集 API,直到拿到可用 JSON
- 使用硬编码主分类、子分类和筛选项
- 首页返回分类、筛选和推荐列表
- 分类支持类型筛选和分页
- 搜索支持分页与空关键词保护
- 详情组装基础元数据与多线路播放列表
- 播放接口区分直链与待解析链接
本次实现不包含:
- 参考 JS 中的刮削重命名
- 弹幕匹配
- 嗅探能力
- 播放页探测与二次解析页面构造
- 修改
base/公共层 - 真实联网集成测试
方案选择
采用“仓库风格重写”的方案,而不是直接把 JS 逻辑逐行搬到 Python。
原因如下:
- 当前仓库是 Python 单文件 Spider 模式,直接适配更一致
- 用户已明确只要基础采集,没必要保留 OmniBox 的包装层和状态管理
- 仓库测试习惯更适合覆盖字段映射、fallback 和播放分支,而不是复制 JS 中的运行时行为
模块边界
新增 py/如意资源.py,只在模块内部维护站点逻辑,不修改 base/。
对外实现以下接口:
initgetNamehomeContenthomeVideoContentcategoryContentsearchContentdetailContentplayerContent
模块内部拆分以下 helper:
_request_json- 统一对多个 API 执行 GET 请求和 fallback
_build_query- 拼接 query string,忽略空值
_get_pic_url- 统一补全图片 URL
_format_vod_list- 将列表接口数据映射为仓库统一字段
_page_result- 统一列表/搜索分页返回结构
_resolve_type_id- 根据主类和筛选结果推导最终请求的类型 ID
_parse_play_groups- 把详情接口返回的播放来源和剧集字符串组装成
vod_play_from/vod_play_url
- 把详情接口返回的播放来源和剧集字符串组装成
_is_direct_media_url- 判断播放链接是否为明显的直链媒体地址
站点配置
固定配置来自参考实现:
- 站点名:
如意资源 - API 列表:
https://cj.rycjapi.com/api.php/provide/vodhttps://cj.rytvapi.com/api.php/provide/vodhttps://bycj.rytvapi.com/api.php/provide/vod
- 图片主机回退:
https://ps.ryzypics.comhttps://ry-pic.comhttps://img.lzzyimg.com
- 默认请求头:
User-AgentAccept: application/jsonAccept-Language: zh-CN,zh;q=0.9Referer: https://cj.rycjapi.com/
- 请求超时:
10秒
请求策略:
- 所有采集接口统一使用
GET - 若显式传入完整 URL,则直接请求该 URL
- 否则按 API 列表顺序发起请求
- 单个 API 非
200、JSON 解析失败或业务字段异常时,继续尝试下一个 API - 所有 API 均失败时返回空结果而不是抛出未处理异常
分类与筛选设计
分类和筛选直接采用硬编码配置,避免依赖站点分类接口的不稳定性。
主分类固定为:
1电影片2连续剧3综艺片4动漫片35电影解说36体育
子分类映射沿用参考实现,例如:
- 电影片默认落到
7 - 连续剧默认落到
13 - 综艺片默认落到
25 - 动漫片默认落到
29 - 体育默认落到
37
homeContent 返回:
classfilters
同时补充首页推荐列表,便于与仓库中支持推荐的蜘蛛保持一致。
筛选结构遵循当前仓库常见格式:
- 每个主分类下只暴露一个
key=type name=类型value为该主分类可选的硬编码子分类
无子分类的主类不返回筛选项。
homeVideoContent 保持仓库现有习惯,返回:
{"list": []}
首页、分类与搜索设计
首页推荐接口使用:
ac=listpg=1pagesize=20
分类接口使用:
ac=listt=<最终类型ID>pg=<页码>pagesize=20
最终类型 ID 的推导规则:
- 若
extend.type存在且非空,则直接使用该值 - 否则若主分类存在子分类映射,则使用该主类的第一个子分类
- 否则使用主分类 ID 本身
搜索接口使用:
ac=listwd=<关键词>pg=<页码>pagesize=30
搜索结果保留接口返回的列表映射,但会额外按标题包含关键词做一次本地过滤,尽量贴近参考实现。
返回结构遵循当前仓库约定:
pagelimittotallist
其中:
- 首页推荐只返回
list - 分类的
limit固定为20 - 搜索的
limit固定为30 - 默认不返回
pagecount
列表字段映射
首页推荐、分类和搜索统一映射为:
vod_idvod_id
vod_namevod_name
vod_pic- 优先使用
vod_pic,必要时补全为完整 URL
- 优先使用
vod_remarks- 优先使用
vod_remarks,否则退回年份
- 优先使用
vod_yearvod_year
type_idtype_id
数据清洗规则:
- 过滤空对象、空
vod_id和vod_id=0 vod_name为空时回退为“未知标题”- 图片字段为
<nil>、nil、null或空值时返回空字符串 - 图片字段为相对路径时,拼接第一个图片主机
详情设计
详情接口使用:
ac=videolistids=<vod_id>
详情返回只保留当前仓库需要的核心字段:
vod_idvod_namevod_pictype_namevod_yearvod_areavod_remarksvod_actorvod_directorvod_contentvod_play_fromvod_play_url
播放列表解析规则:
- 从
vod_play_from读取线路名,逗号分隔 - 从
vod_play_url读取分集,#分隔 - 每个分集项按
标题$地址解析 - 若分集缺少标题,则回退为
第N集 - 若缺少地址,则跳过
为了兼容仓库现有数据结构,详情输出不保留 JS 版的嵌套 vod_play_sources,而是直接组装为:
vod_play_from: 用$$$连接的线路名vod_play_url: 与线路一一对应、用$$$分组、每组内部用#连接的剧集串
若参考站点只返回一组剧集但含多个线路名,则默认把同一组剧集复制到每个线路名下,以保持与参考实现的可播放行为一致。
播放设计
播放阶段只做基础采集,不做嗅探和刮削增强。
输入为详情里拼出的剧集地址。处理规则如下:
- 若地址为空,返回空播放结果
- 若地址是明显的媒体直链,返回:
parse=0playUrl=""url=<直链>
- 若地址是普通
http/https链接但不是明显媒体直链,返回:parse=1playUrl=""url=<原始链接>
- 若地址不是 URL,仍按待解析地址返回,交给上游处理
明显媒体直链的判定后缀包括:
.m3u8.mp4.flv.avi.mkv.ts
本次实现不为播放结果附加自定义 header,除非测试或站点字段证明有明确必需值。
错误处理
错误处理原则是“接口尽量返回空结果,不向外抛异常”。
具体策略:
- 请求失败时继续 fallback
- 分类、首页、搜索失败时返回空列表
- 详情失败时返回
{"list": []} - 播放失败时返回
{"parse": 0, "playUrl": "", "url": "", "header": {}} - 局部字段缺失只影响对应字段,不使整条记录失效
测试设计
测试文件新增为 py/tests/test_如意资源.py,全部使用 unittest 和 mock,不依赖真实网络。
首轮测试覆盖:
homeContent暴露硬编码分类和筛选_get_pic_url处理绝对路径、相对路径和空值_request_json在首个 API 失败时会 fallback 到下一个 APIhomeContent或首页推荐正确映射列表字段categoryContent能从主类推导默认子类,也能接受extend.typesearchContent对空关键词直接返回空结果searchContent正确传递搜索参数并做标题过滤detailContent正确映射基础字段并拼出vod_play_from/vod_play_urlplayerContent对直链返回parse=0playerContent对非直链 URL 返回parse=1
测试遵循 TDD:
- 先写失败测试
- 确认失败原因正确
- 再补最小实现
- 最后跑目标模块测试
验收标准
完成后应满足以下标准:
- 新蜘蛛文件和对应测试文件均已存在
- 所有新增测试离线可运行
- 分类、推荐、分类列表、搜索、详情、播放六类基础能力可用
- 返回结构符合当前仓库 Spider 约定
- 不引入刮削、弹幕、嗅探等额外行为