Files
tvboxzt/py/docs/superpowers/specs/2026-04-19-gimy-spider-design.md
T

10 KiB
Raw Blame History

Gimy剧迷 Python 爬虫设计

目标

在当前 Python 仓库中新增一个符合 base.spider.Spider 接口的 Gimy剧迷站点爬虫,覆盖以下能力:

  • 首页分类
  • 首页推荐视频
  • 分类列表
  • 搜索
  • 详情解析
  • 播放解析

实现基于网页 HTML 抓取与播放页脚本解析,不依赖 Playwright,不修改 base/ 公共层。

范围

本次实现包含:

  • 新增独立脚本,文件名为 剧迷.py
  • 使用单一站点主域:https://gimyai.tw
  • 支持 home/homeVideo/category/detail/search/player 全链路
  • 支持分类排序筛选
  • 支持多线路详情播放列表
  • 支持搜索结果精筛
  • 支持播放页直链提取与 parse.php 回退解析

本次实现不包含:

  • 多域名自动回退
  • Cloudflare 或其他风控绕过
  • 第三方繁转简依赖
  • 参考 JS 中全部兼容分支的 1:1 平移
  • 通用电影网站抽象层

方案选择

采用仓库风格的单文件拆分方案:

  • 对外保持当前仓库统一的 Spider 接口
  • 对内拆成列表解析、详情解析、搜索精筛、播放解析、文本清洗与繁转简几个独立 helper
  • 优先复用当前仓库里 HTML 站点的实现风格,而不是逐行翻译参考 JS

不做逐行平移的原因是:

  • 当前仓库以单站点单文件为主,便于维护和测试
  • 参考 JS 中包含动态依赖探测和运行时日志逻辑,Python 版只保留真正影响解析结果的核心链路
  • 本次目标是先把常见场景稳定覆盖,而不是追求所有历史兼容分支同步迁移

模块边界

新增脚本只在站点文件内部维护逻辑,不修改 base/

脚本内部职责拆分如下:

  • init
    • 初始化主域、请求头、分类配置、筛选配置
  • homeContent
    • 返回固定分类与筛选定义
  • homeVideoContent
    • 抓取首页卡片,返回首页推荐列表
  • categoryContent
    • 构造分类页 URL 并解析卡片
  • detailContent
    • 请求详情页并整理影片元信息与多线路剧集列表
  • searchContent
    • 请求站内搜索页,解析原始结果并做关键词精筛
  • playerContent
    • 解析播放页脚本,优先直取媒体地址,失败时回退到 parse.php 或播放页
  • 私有辅助函数
    • URL 归一化
    • HTML 文本清洗
    • 精简繁转简映射
    • 列表卡片解析
    • 详情字段提取
    • 播放页脚本提取和 URL 解码
    • 搜索关键词归一化与打分

Host 与请求策略

本次只实现单域:

  • https://gimyai.tw

请求头固定为浏览器样式,至少包含:

  • User-Agent
  • Accept-Language
  • Referer
  • Accept

对播放页和 parse.php 请求,按需要补充:

  • Origin

请求策略保持轻量:

  • 所有页面请求统一走 self.fetch
  • 默认超时取 10 秒到 20 秒之间的站点级固定值
  • 非 200 响应统一回退为空 HTML 或在播放解析时进入回退流程

不做:

  • host 探活切换
  • 重试队列
  • 代理层改造

分类与筛选设计

分类固定为:

  • 1 -> 电影
  • 2 -> 电视剧
  • 4 -> 动漫
  • 29 -> 综艺
  • 34 -> 短剧
  • 13 -> 陆剧

筛选只支持排序字段 by,候选值为:

  • time
  • hits
  • score

homeContent 返回:

  • class
  • filters

其中 filter_def 只在站点内部用于默认值,不额外暴露到返回结构。

首页与列表设计

首页推荐

homeVideoContent 直接请求首页 HTML,并复用列表卡片解析逻辑。

卡片解析范围:

  • 详情链接包含 /detail/
  • 标题优先取 title
  • 回退 img alt
  • 再回退 .title.video-text、标题标签文本或卡片文本
  • 封面优先取 lazyload 属性,再回退 img src 或背景图样式
  • 备注从卡片或父容器文本中抓取常见状态文案,例如:
    • 更新至第X集
    • 全X集
    • HD
    • TC
    • 抢先版

首页推荐只返回前 24 条,保持与参考代码一致。

分类列表

分类 URL 模式:

  • /genre/<tid>.html
  • page > 1 或排序不为 time 时追加 ?page=<pg>&by=<sort>

返回分页字段:

  • page = 当前页
  • pagecount = pg + 1,当页命中数量达到列表页常规规模时递增
  • limit = 20
  • total = pg * 20 + 当前条数

这里的总数是近似值,和当前仓库其他 HTML 站点的处理方式保持一致,不承诺真实总数。

文本清洗与繁转简

站点内容与搜索结果里可能混有繁体字。为了保证搜索命中率和展示稳定性,脚本内部会提供两层处理:

  • normalizeText
    • 去 HTML 标签
    • &nbsp;
    • 压缩空白
  • convertTraditionalToSimplified
    • 使用内置映射表做精简繁转简

不引入 opencc 或其他第三方库,原因是:

  • 当前仓库未配置相关依赖
  • 本次只需要覆盖站点常见影视字段和搜索词,不需要通用全文转换
  • 减少安装和运行环境差异

映射表优先覆盖:

  • 影视分类词
  • 常见标题用字
  • 搜索噪声词

展示用文本通过 toDisplayText 统一进入“清洗 + 繁转简”链路。

搜索设计

搜索 URL

  • /find/-------------.html?wd=<keyword>&page=<pg>

搜索流程分两步:

  1. 解析原始卡片列表
  2. 对原始列表做关键词精筛

关键词精筛策略保留参考实现的核心思路:

  • 对关键词和片名统一做繁转简
  • 去空格、破折号、标点和括号
  • 移除常见噪声后缀,例如:
    • 线上看
    • 全集
    • 连续剧
    • 电视剧
    • 动漫
    • 电影
    • 综艺
  • 衍生 token
    • 原始归一化词
    • 去噪声词后的词
    • 去掉结尾集数或纯数字后的词
  • 打分规则:
    • 完全相等最高
    • 前缀匹配次之
    • 包含匹配再次之
    • 片名较短且被 token 包含作为弱命中

若打分结果为空,再回退到宽松包含匹配,避免误把合法结果全部过滤掉。

详情页设计

详情 URL 模式:

  • /detail/<id>.html

detailContent 支持传入:

  • 纯数字详情 id
  • 完整详情 URL

输出字段包含:

  • vod_id
  • vod_name
  • vod_pic
  • vod_content
  • vod_remarks
  • type_name
  • vod_year
  • vod_area
  • vod_actor
  • vod_director
  • vod_play_from
  • vod_play_url

字段提取策略:

  • 标题优先取详情页主标题,回退页面 <title>
  • 封面优先取 og:image,再回退详情图区域图片
  • 剧情简介优先取详情正文区域,回退 meta description
  • 状态类别年代国家/地区 通过标签名前缀匹配提取
  • 主演导演 优先从带链接的列表区域提取并以逗号连接

多线路播放列表

详情页的线路和剧集采用 tab + playlist 容器结构:

  • 线路名从 #playTab a[href^='#con_playlist_'] 提取
  • 每个 tab 内的剧集链接从 /play/<id>.html 提取
  • 单集格式为 集名$play_id

最终按当前仓库约定输出:

  • vod_play_from = 线路A$$$线路B
  • vod_play_url = 第1集$100-1-1#第2集$100-1-2$$$正片$100-2-1

播放解析设计

播放 URL 模式:

  • /play/<play_id>.html

playerContent 支持传入:

  • 紧凑 play_id
  • 完整播放页 URL

播放页数据提取

核心信息来自页面脚本中的 player_data

  • url
  • encrypt
  • from

提取方式:

  • 用正则抓取 var player_data = {...}
  • JSON 解析失败则视为无数据

URL 解码策略

保留参考实现的解码规则:

  • encrypt = 0
    • 原样使用
  • encrypt = 1
    • URL decode
  • encrypt = 2
    • base64 decode 后再尝试 URL decode

播放优先级

  1. 若解码后的 rawUrl 已经是以 m3u8/mp4/flv/m4s 结尾的媒体地址,直接返回:
    • parse = 0
    • url = rawUrl
  2. rawUrl 非空但不是直接媒体地址,则根据线路名构造解析器地址:
    • 默认 https://play.gimyai.tw/v/parse.php
    • JD4K/JD2K/JDHG/JDQM/d/parse.php
    • NSYS/n/parse.php
  3. parse.php 返回 JSON,优先读取:
    • url
    • video
    • playurl
  4. 若解析器没有给出直链,但 rawUrl 自身是 http 地址,则返回:
    • parse = 1
    • jx = 1
    • url = rawUrl
  5. 全部失败时回退返回播放页地址,保证行为可用:
    • parse = 1
    • jx = 1
    • url = 播放页 URL

返回头策略

直链成功时按场景返回最小必要头:

  • 直接媒体地址:
    • 保留浏览器 User-Agent
    • Referer = 播放页
  • parse.php 成功:
    • Referer = 对应解析页
    • Origin = 解析页 origin

错误处理策略

站点脚本应尽量返回空结构或回退值,而不是把异常直接抛到上层。

原则如下:

  • 列表页请求失败时返回空列表分页
  • 详情页单条失败时可跳过当前 id,避免批量请求全失败
  • 播放解析失败时优先回退到播放页 URL
  • JSON 解析失败一律回退空字典或空字符串

不做细粒度错误分类,只做站点级稳妥回退。

测试设计

新增测试文件:

  • tests/test_剧迷.py

测试只做离线单元测试,使用 patch mock 网络请求,不依赖真实站点。

重点覆盖以下行为:

  1. homeContent
    • 返回固定分类和 filters
  2. 列表卡片解析
    • 能提取 vod_idvod_namevod_picvod_remarks
  3. detailContent
    • 能解析基础字段和多线路播放列表
  4. 搜索精筛
    • 简体关键词能命中繁体标题
    • 噪声后缀不会挤掉正确结果
  5. playerContent
    • 直接媒体地址时返回 parse=0
    • parse.php 成功时返回解析后的直链
    • parse.php 失败时能回退到原始 URL 或播放页
  6. 解码辅助函数
    • 覆盖 encrypt=0/1/2

测试风格对齐:

  • tests/test_dbku.py
  • tests/test_youknow.py

非目标与风险

本次实现明确不覆盖:

  • 多 host 灾备
  • 强风控页面或验证码绕过
  • 站点未来改版后的自适应恢复
  • 通用繁转简库级别的高完整度转换

主要风险如下:

  • Gimy 的 DOM 类名若调整,列表和详情解析会失效
  • 播放解析依赖 player_dataparse.php 的现有格式,若接口切换需单独修复
  • 精简繁转简映射只覆盖常见影视词,极少数片名可能仍然需要宽松匹配兜底

实施顺序

推荐实施顺序如下:

  1. 先写 tests/test_剧迷.py,覆盖卡片、详情、搜索和播放关键行为
  2. 运行测试确认红灯
  3. 实现 剧迷.py 的基础结构与 helper
  4. 逐步补齐搜索精筛与播放解析,直到测试转绿
  5. 最后运行站点测试集合做回归确认