番茄小说榜单扫描与开书决策
针对用户在番茄小说指定的频道 + 类目做榜单扫描,把「现在什么火」翻译成「我该开什么书」。 交付三件套:扫榜报告(数据 + 分析)、开书建议(可执行选题)、扫榜长图(可分享 PNG)。
判定一切结论的唯一标准:是否有利于在番茄拿到留存与推荐量。番茄是免费阅读 + 广告变现 + 纯算法驱动平台,只按热度、供给、差异化排序,不按文学性或作者表达欲排序。
硬性交互契约
- 先确认参数,再动手。频道与类目由用户指定,不得自行假设。缺失时必须弹框询问(见步骤 0)。
- 数据来源双接口,缺一不可。番茄官网对榜单页的书名、简介做了PUA 码位编码(替换为 Unicode 私用区 PUA 字符),但作品详情接口返回的文本完全为明文(实测 300 本 PUA 残留 = 0)。因此必须同时走两个官方 JSON 接口:榜单接口拿排名 / 在读 / 状态 / 字数 / 更新时间,作品详情接口还原书名 / 作者 / 简介 / 官方标签,再按 bookId 对齐(见步骤 2)。不需要浏览器,也不需要截图 OCR。
- 一切数据标注口径。官方一手数据、行业观察、模型推断三类必须分开标注,不得混为一谈,不得把行业观察当官方数据陈述。
- 不代写可投稿正文。只产出数据、分析与选题层面的方案,正文由用户自行完成。
- 每一步的关键产出(
rank-data.json路径、接口原始响应、统计结果、长图 spec)都要落到磁盘并告知用户,不能只停留在对话里。
环境准备(首次使用或脚本报缺依赖时)
本 skill 的脚本走独立 Python 虚拟环境,不污染用户环境。下文 <skill> 指本 skill 的安装目录,<PY> 指虚拟环境解释器:
<PY> = C:/Users/Administrator/.workbuddy/binaries/python/envs/default/Scripts/python.exe
若该解释器不存在,或运行脚本时报 缺少 Pillow:
& "C:\Users\Administrator\.workbuddy\binaries\python\versions\3.13.12\python.exe" -m venv "C:\Users\Administrator\.workbuddy\binaries\python\envs\default"
& "<PY>" -m pip install --disable-pip-version-check Pillow
中文字体自动探测 C:\Windows\Fonts\msyh.ttc(微软雅黑),Windows 下默认可用。脚本报「未找到可用中文字体」时,检查该字体是否存在。
抓取环节零依赖:步骤 2 的双接口抓取只用 Python 标准库(urllib / json / concurrent.futures),无需安装任何包。只有渲染长图需要 Pillow。
产出 .docx 的依赖(步骤 6 走 tencent-docx 时):html-to-docx 转换引擎需要 python-docx / html-for-docx / beautifulsoup4 / lxml / httpx / click;把 Markdown 终稿转 HTML 还需要 markdown。这些一并装进同一个 venv:
& "<PY>" -m pip install --disable-pip-version-check "python-docx>=1.1,<2" "html-for-docx>=1.1,<2" "beautifulsoup4>=4.12,<5" "lxml>=5.2,<6" "httpx>=0.27,<1" "click>=8.1,<9" markdown
Windows 上 html-to-docx 的官方 setup-html-to-docx.sh 是 Bash 脚本、且默认走 ~/.venv-html-to-docx(Unix 路径布局),在纯 Windows 环境不可直接用。直接用上面的 venv + PYTHONPATH 指向 <plugin>/skills/html-to-docx/scripts 调用即可。
步骤 0 — 参数确认(不可跳过)
先弹框询问,一次问完;用户已在上文明确给出的项不再重复问。
| 参数 | 说明 | 默认 |
|---|---|---|
| 频道 | 男频 / 女频 | 必填,无默认 |
| 类目 | 该类目下的具体分类,名称见 references/fanqie-rank-map.md 的类目表 | 必填,无默认 |
| 榜单 | 阅读榜 / 新书榜 / 两个都要 | 两个都要 |
| 样本量 | 每榜取前 N 名 | 50 |
| 抽样读正文 | 是否抽样打开 8–12 本作品看目录与前几章 | 关闭(开启会显著变慢) |
| 输出目录 | 产物落盘位置 | <workspace>/扫榜_<类目>_<YYYYMMDD>/ |
补充规则:
- 类目如果用户说的名字在类目表里找不到(例如说「都市」但表里是「都市日常 / 都市脑洞 / 都市修真 / 都市高武 / 都市种田」),列出该频道下的相近类目让用户选,不要自己挑一个。
- 用户只说「男频扫榜」没给类目时,列出该频道全部类目并说明「建议一次扫 2–3 个相邻类目做横向对比」,由用户决定。
步骤 1 — 定位榜单 URL
URL 规则:https://fanqienovel.com/rank/{频道码}_{榜单码}_{类目ID}
- 频道码:
1= 男频,0= 女频 - 榜单码:
2= 阅读榜,1= 新书榜 - 类目 ID:见
references/fanqie-rank-map.md(含男频 19 个、女频 18 个类目的完整 ID 表)
例:https://fanqienovel.com/rank/1_2_504 = 男频阅读榜 · 抗战谍战。
类目 ID 会随平台调整变化,禁止长期硬编码。每次扫榜的第一步都是校验,有两条路(任选其一,推荐第二条):
- 抓一次榜单页 HTML,用正则
/rank/(\d+)_(\d+)_(\d+)抽出当前导航里的真实链接与类目名,与参考表比对; - 抓一次榜单页 HTML,解析
window.__INITIAL_STATE__.rank.rankCategoryTypeList——它是{male:[{id,name},...], female:[...]}结构,一次请求就能拿到该频道全部类目的权威 ID 与名称,不需要逐个类目去点。注意:这个字段只在页面内嵌状态里,不在/api/rank/category/list的响应里(该接口只返回book_list/total_num/rankVersion/rankTypeText)。
不一致时以页面为准,并把新 ID 写回参考表。scripts/crawl_rank.py 已内置这一步(页面解析失败时回落内置类目表)。
榜单机制(引自官网榜单说明,扫榜时原样引用到报告里):
作品按照其在番茄小说中的分类进行划分排榜,排榜顺序按照在读数据排序,仅排 1000 在读以上的作品 阅读榜:30 万字以上、已签约未下架、已经开始推荐的番茄原创作品 新书榜:30 万字以下、已签约未下架、已经开始推荐的且未断更,完结未超过 90 天的番茄原创作品 排行榜每天下午 3 点前更新截止到上一日的排名数据
(以上为官网原文,原文无句末标点,引用时不要自行添加引号或改写词句。)
由此得到两条必须写进结论的推论:
- 新书榜反映当下正在起量的题材(30 万字以下 = 近期开书),阅读榜反映已被验证的存量题材。判断「该开什么」必须两榜交叉看,只看一个榜会得出相反结论。
- 榜单每天 15:00 前更新到前一日,必须在 15:00 之后抓取,并记录「统计截止日期」。15:00 前抓到的数据是两天前的。
步骤 2 — 抓取(双接口,必须都走)
番茄官网对榜单页 HTML 的书名、简介做了PUA 编码(私用区 PUA 字符),但作品详情接口返回的文本完全未编码。因此不要走「截图 + OCR」——直接调两个官方 JSON 接口,快、准、零识别误差(实测 300 本 PUA 残留 = 0)。
一条命令跑完抓取(推荐,脚本已把下面的细节全部实现,含分页、编码还原、并发、校验):
& "<PY>" "<skill>/scripts/crawl_rank.py" --channel 1 --category 1140,258,257 --lists 2,1 --top 50 --outdir "<输出目录>"
产出 <输出目录>/rank-data.json、raw_json/(接口原始响应)、crawl_log.txt(含校验结果)。下面的接口细节供排错与手工调用时参考。
接口 A|榜单列表(拿排名与在读数)
GET https://fanqienovel.com/api/rank/category/list
?app_id=1967&rank_list_type=3
&offset={0,10,20,...}&limit=10
&category_id={类目ID}&rank_version={上一页返回的 rankVersion 或空}
&gender=1&rankMold={2=阅读榜, 1=新书榜}
gender:1 = 男频,0 = 女频。rankMold与 URL 第二段一致(/rank/1_2_1140→ gender=1, rankMold=2)。- 必须在同一个榜单内先把第一页拿到的
rankVersion回填给后续offset的请求,保证分页取自同一期快照。 - 返回
{code:0, data:{book_list:[...], total_num, rankVersion, rankTypeText}}。total_num通常为 100(榜单共 100 条)。 - 每页固定 10 条,取前 N 名就请求
ceil(N/10)次。 - 早期用浏览器抓页面只能拿到 SSR 的前 10 条,且
readCount是占位的"0"——真实在读数在read_count字段(如"664036"= 66.4 万),不要用错字段。
⚠ 最易踩的坑:
rank_version参数「可以为空,但不可以不存在」。 实测同一组参数连续 8 次:带&rank_version=(值为空)稳定返回n=10, total_num=100; 整个参数省略则返回code=0, n=0, total_num=0——一个不报错的「假成功」, 极易被误判成「该类目没数据」或「接口已下线」。请求头(UA、Accept-Language)完全不影响结果。 排查榜单接口空列表时,第一件事就是确认rank_version在 URL 里(哪怕为空)。
book_list 每个条目的可靠字段:
| 字段 | 说明 |
|---|---|
| currentPos | 排名 |
| rankPosDiff | 排名变化(0 = 无变化,正数 = 上升名次) |
| read_count | 真实在读数(字符串,形如 "664036")——最重要的热度指标 |
| creationStatus | "0" = 已完结,"1" = 连载中 |
| wordNumber | 总字数(字符串)。阅读榜必然 ≥ 30 万,新书榜必然 < 30 万,可用来校验 rankMold 有没有接反 |
| lastChapterTitle | 最新章节标题(未编码,可直接用) |
| lastChapterUpdateTime | Unix 秒级时间戳,需自行转本地时间 |
| bookId | 跨接口对齐与去重的主键 |
| firstChapterItemId | 首章 itemId,可用于进一步抓目录 |
| bookName / author / abstract | 已被PUA 编码,不可引用,一律用接口 B 覆盖 |
接口 B|作品详情(还原书名、作者、简介、官方标签)
GET https://fanqienovel.com/api/book/info?bookId={bookId}
注意参数名是驼峰 bookId,写成 book_id 会返回 {"code":-1,"message":"没有bookId"}。
返回 data 中的关键字段(全部为未编码文本):
| 字段 | 说明 |
|---|---|
| bookName / authorName | 干净书名与作者名 |
| abstract | 干净简介 |
| categoryV2 | 官方标签数组(JSON 字符串),每个元素含 Name / ObjectId / Dim / Gender / ExternalDesc。这是最可靠的标签来源,比作者自填的【】标签更能反映平台归类 |
| wordNumber / creationStatus / status | 字数与状态 |
| lastChapterTitle / lastPublishTime | 最新章节与发布时间 |
| thumbUri | 封面图 |
性能与并发:单次请求约 0.4 秒。用 ThreadPoolExecutor(max_workers=6) 并发,300 本约 2 分钟。请求间隔留 1–2 秒重试退避即可,实测无频率限制。
落盘格式:写成 rank-data.json,schema 见 assets/rank-data.example.json。建议同时把接口原始响应按 raw_json/rank_...json 与 raw_json/book_{bookId}.json 落盘,便于复核与后续差分。抓完立即落盘,避免上下文丢失后重抓。
对齐规则:两个接口用 bookId 1:1 对齐(不是排名——排名会随榜单刷新变动)。接口 B 取不到的条目,在报告中标注该条书名来自接口 A 的编码文本并如实说明缺字。
校验清单(抓完必做):
- 每个榜单实际条数 = 目标样本量;
- 书名 / 作者 / 简介中 PUA 字符(U+E000–U+F8FF)总数为 0;
- 阅读榜所有作品
wordNumber ≥ 300000、新书榜所有作品wordNumber < 300000(校验rankMold未接反); - 同一榜单内书名去重后条数 = 样本量。
步骤 3 — 联网补全
用 WebFetch / WebSearch 检索以下信息,作为「行业观察」类证据,与官网数据分区标注:
- 「番茄小说 <类目> <当前年月>」→ 近月题材动向、平台活动、征文赛道
- 番茄官方公告(低质内容治理、发文规则、福利活动)→ 影响开书合规与收益预期
- 该品类在女频/男频另一侧的对应题材热度 → 判断题材迁移机会
搜索结果一律标注「行业观察,自媒体口径,仅作趋势参考」。
步骤 4 — 统计与抽样
必做统计(用脚本,不要手算):
# 数据只含一个榜单
& "<PY>" "<skill>/scripts/rank_stats.py" --input rank-data.json --outdir .
# 数据含多个榜单(如 --lists 2,1 双榜):必须用 --list-index 指定,阅读榜与新书榜不可混合统计
& "<PY>" "<skill>/scripts/rank_stats.py" --input rank-data.json --outdir . --list-index 0 --prefix stats_read
& "<PY>" "<skill>/scripts/rank_stats.py" --input rank-data.json --outdir . --list-index 1 --prefix stats_new
不带 --list-index 而数据含多榜时,脚本会报错并列出各榜的 channel/category/list 与条数,让你按需指定——
这是有意设计:把阅读榜(30 万字以上存量)与新书榜(30 万字以下增量)合并统计会得出错误的题材结论。
可用 --list-index 前先跑一次不带参数的,照它列出的清单选。
脚本兼容两种 rank-data.json 形状:扁平的 {meta, items} 与分榜的 {meta, lists:[{items}]}。
产出 stats.json + stats.md,覆盖:在读梯队分布、头部集中度(Top3/Top10 占比)、连载完结比例、更新时效、标签频次、书名长度与词频、数字/标点使用率。
脚本只做确定性统计,不做判断。判断由步骤 5 完成。
可选抽样(用户在步骤 0 开启时):从榜单中挑 8–12 本/类目(头部 3 本 + 中位 2 本 + 新书榜头部 3 本 + 新书榜末位 1 本),读取每本的完整目录并记录结构指标。
抽样方法:请求 https://fanqienovel.com/page/{bookId} 取 HTML(未编码),用正则抽出目录条目:
re.findall(r'\{"itemId":"(\d+)","needPay":\d+,"title":"(.*?)","isChapterLock":\w+,'
r'"isPaidPublication":\w+,"isPaidStory":\w+,"volume_name":"(.*?)",'
r'"realChapterOrder":"(\d+)","firstPassTime":"(\d+)"\}', html)
由此可稳定算出(全部是硬数据,不依赖读正文):
| 指标 | 算法 |
|---|---|
| 章节数 / 卷数 | 目录条目数 / volume_name 去重数(可看出该品类是否普遍不设卷) |
| 平均章节字数 | wordNumber ÷ 章节数 |
| 跨时天数 | (末章 firstPassTime − 首章 firstPassTime) ÷ 86400 |
| 章/天、日更字数 | 章节数 ÷ 跨时天数、wordNumber ÷ 跨时天数 |
| 首 5 章标题 | 用于判读开篇设定与金手指时机 |
重要限制:番茄的章节正文接口(/api/reader/*)要求登录,会返回「请先登录」。因此**「开篇第几段出冲突」「章末钩子形态」这类段级分析本次无法完成**,必须在报告中如实标注为未取数,并说明以目录结构 + 首章标题 + 简介替代、置信度为「中」。不要凭简介想象正文内容。
抽样结论必须标注样本量,不得当作全量事实。
步骤 5 — 分析(20 维,逐条过)
完整维度定义与判读标准见 references/scan-dimensions.md,按维度分段读取,不要整篇灌入。
五大组:结构热度(在读结构/换血率/活跃度)、题材差异化(标签分布/稀缺组合/跨界迁移)、包装层(书名公式/高频词/简介钩子/标签组合)、内容层(开篇设定/金手指形态/主角原型/章节节奏/篇幅体量)、判断层(蓝海红海矩阵/可开选题)。
分析必须遵守两条:
- 每个结论后面挂数据。写「悬疑灵异类目被系统流占满」必须给出「Top 50 中 31 本带系统标签,占比 62%」。
- 区分「已被验证」与「正在起量」。阅读榜高位 + 新书榜无同类 = 存量被验证但新供给少,是蓝海信号;两榜都挤满 = 红海,需要靠差异化变量切入。
步骤 6 — 产出三件套
三份产物的完整结构规范见 references/output-specs.md,严格按结构输出,不得省略标题。
① 扫榜报告(必做)→ 走 tencent-docx skill 生成 .docx
文件名:扫榜报告_<类目>_<YYYYMMDD>.docx
内容:数据面板 → 5 条核心结论 → Top N 原始数据表 → 热度结构分析 → 题材标签分布 → 书名与简介包装规律 → 内容层规律(若有抽样)→ 竞品对标表 → 蓝海红海矩阵 → 数据口径与免责声明。
② 开书建议(必做,单独成文)→ 走 tencent-docx skill 生成 .docx
文件名:开书建议_<类目>_<YYYYMMDD>.docx
内容:结论先行(主推方案 + 分档总览)→ 星级评分体系说明(六维锚点,先讲清尺子)→ 方案汇总对照表 → 5–10 个开书方案(按综合星级降序,每个含星级评分表 / 一句话卖点 / 书名 3 选 / 300 字内简介 / 主角与金手指含代价与失效条件 / 差异化变量 / 对标作品与雷同风险 / 目标读者与预期体量 / 风险与规避 / 为什么排在这个位次)→ 分档结论(首选 / 次选 / 备选 / 探索)→ 首发包装清单 → 更新与运营建议 → 与「番茄小说大纲审查」skill 的衔接建议。
星级规则:六维(赛道热度 / 供给缺口 / 差异化强度 / 留存潜力 / 执行难度 / 变现可行性)各 1–5 星,每维必须给判据并挂数据,禁止只打星不说理由;执行难度为反向计分(越好写星越高);综合星级取六维等权平均,保留到 0.5 星并附百分制。综合星级只用于排序,不替代风险判断。锚点定义见 references/output-specs.md。
数量规则:在 5–10 个之间自适应,不预设固定数量——由该类目实际存在几个有真实区分度的可开方向决定:方向多的类目写到 8–10 个,方向少的类目写 5 个即可。任何情况下不得少于 5 个、不得多于 10 个,不允许同一设定换皮(换书名、换金手指名词但核心变量相同)。若穷尽后确实凑不满 5 个有区分度的方案,如实说明该品类可开方向有限并给出实际数量,不要灌水凑数。
③ 扫榜长图(必做)→ 用脚本渲染 PNG
先按 assets/longimage-spec.example.json 的 schema 写出 longimage-spec.json,再渲染:
& "<PY>" "<skill>/scripts/render_longimage.py" --spec longimage-spec.json --out "扫榜长图_<类目>_<YYYYMMDD>.png"
可选参数:--width(逻辑宽度,默认 750,分享友好)、--scale(渲染倍率,默认 2)。
长图只放最硬的信息,控制在 8–12 屏:头部信息 → 4 个数字卡 → 子题材分布条形图 → TOP 10 榜单 → 书名高频词 → 开书方案 Top 3(带综合星级,并注明共几个方案)→ 数据来源与免责声明。
若 Pillow 不可用且无法安装,降级为「生成同名 HTML 长页 + 用 present_files 预览」,并在交付说明中明确告知未产出 PNG。
步骤 7 — 交付与衔接
用 present_files 一次性呈现三个文件(长图放第一位)。
交付后主动给出一句衔接建议:选定选题可交给 番茄小说大纲审查(男频修仙/玄幻/仙侠)或 番茄文娱小说大纲审查(文娱类)做全维度体检;不在覆盖范围内的类目,说明只能给通则建议。
数据口径与红线
- 在读量级:番茄官方口径,单位为「万」,是榜单排序依据;榜单仅收录 1000 在读以上作品,因此榜单数据对冷启动作品完全不可见,分析尾部时必须在报告中说明这一点。
- 口径分区:
【官网数据】/【行业观察】/【模型推断】三种前缀必须出现在报告对应段落。 - 文本来源说明:书名、作者、简介与标签来自作品详情接口的原生文本,未经截图或 OCR,不存在识别误差;报告中要如实写「书名与标签通过作品详情接口还原,未经截图识别」,不要再沿用「经截图视觉识别补全,可能存在识别误差」的旧表述。
- 涉及平台红线(涉政、涉黄、过度血腥、未成年人相关不当内容)的题材方向,直接指出并给替换方向,不帮忙包装。
- 不承诺收益、不预测具体在读数字、不伪造「内部榜单」或非公开数据。
容错与降级
| 情况 | 处理 |
|---|---|
| 类目名对不上 | 列出该频道相近类目让用户选,不自行匹配 |
| 榜单接口返回 book_list 为空(code=0 但 n=0) | 九成是 URL 里漏了 rank_version 参数——该参数必须存在(值可空),否则稳定返回空列表且不报错。其次检查 category_id / rankMold 是否与目标榜一致、rank_version 是否被上一组参数污染 |
| 榜单接口返 404 / 空 body | 该类目 ID 可能已调整,回 references/fanqie-rank-map.md 用 rankCategoryTypeList 重新取 ID |
| 榜单接口返回 {"code":-1,...} | 读 message 字段定位缺哪个参数(最常见的两个坑:作品详情接口的 bookId 写成了 book_id;榜单接口漏了 app_id) |
| 作品详情接口取不到某本 | 记为缺失并如实报告,不用接口 A 的编码书名冒充;该条在报告中标注「书名来自榜单接口原始文本,存在缺字」 |
| 阅读榜出现 <30 万字 / 新书榜出现 >30 万字的作品 | rankMold 接反了(2=阅读榜、1=新书榜),不是平台数据异常 |
| 单个请求超时 / 连接重置 | 串行重试 3 次、退避 1.2s×n;并发抓 300 本时偶发失败属正常,失败清单单独重跑 |
| 章节正文读不到 | 正常现象:/api/reader/* 系列接口要求登录(返回「请先登录」)。内容层分析改用「/page/{bookId} 页面里的完整目录」——章名与 firstPassTime 未编码,可算章节数、卷数、平均章节字数与日更字数 |
| 类目名对不上 | 列出该频道相近类目让用户选,不自行匹配 |
| 用户在 15:00 前要求扫榜 | 明确告知当期数据尚未更新,抓到的是前一日数据,并在报告中写明统计截止日期 |
| 样本量不足(该类目上榜作品少于 N) | 如实记录实际样本量,不补位、不虚构 |
| Pillow 不可用 | 按对应步骤的降级路径执行,并明确告知用户哪一环降级了。抓取环节不受影响(只用标准库) |
| 用户要求扫「全站」而非指定类目 | 说明本 skill 以单类目深扫为单位;建议拆成 2–3 个类目分批扫,或先做男频/女频类目总览再选类目深扫 |
脚本自测(改动脚本或首次使用后建议跑一次)
用附带示例数据验证两个脚本能正常出产物,不消耗抓取成本:
& "<PY>" "<skill>/scripts/rank_stats.py" --input "<skill>/assets/rank-data.example.json" --outdir ./_selftest
& "<PY>" "<skill>/scripts/render_longimage.py" --spec "<skill>/assets/longimage-spec.example.json" --out ./_selftest/selftest.png
预期:_selftest/stats.json、_selftest/stats.md、_selftest/selftest.png(1500×4390 左右)三个文件生成成功。自测完删除 _selftest 目录。
长图支持的区块类型:stats(数字卡)、bars(条形分布)、toplist(榜单条目)、cards(卡片)、tags(标签云)、text(段落)、kv(键值行)、steps(编号步骤)。未知类型会直接报错并列出可选值。
资源索引
references/fanqie-rank-map.md—— 榜单体系、URL 规则、完整类目 ID 表、PUA 编码机制、双接口抓取 SOP 与可复用正则references/scan-dimensions.md—— 20 个扫榜分析维度的定义、判读标准与结论写法references/output-specs.md—— 三件套的固定结构与文案规范scripts/crawl_rank.py—— 榜单双接口抓取(主路径):一条命令抓「频道 × 多类目 × 双榜 × 前 N 名」,自动还原文本并跑校验清单,产出rank-data.json。只用标准库,无第三方依赖scripts/rank_stats.py—— 榜单数据确定性统计(在读结构/标签频次/书名词频/更新时效),兼容扁平分榜两种 schemascripts/render_longimage.py—— 长图渲染(JSON spec → PNG)assets/rank-data.example.json—— 榜单数据结构 schemaassets/longimage-spec.example.json—— 长图数据结构 schema
降级备用(接口整体失效时才用)
scripts/fetch_rank.py—— 备用抓取路径:解析榜单页window.__INITIAL_STATE__+?offset=翻页- 区间校验重试 + PUA 字体解码。实测可稳定取到 Top 50(3/3 次成功),但比接口慢、需重试,
仅在
/api/rank/category/list不可用时启用。
- 区间校验重试 + PUA 字体解码。实测可稳定取到 Top 50(3/3 次成功),但比接口慢、需重试,
仅在
assets/pua-map.json—— 页面编码映射表(PUA 码位 → 汉字),已覆盖字体全部 362 个码位。 内容经/api/book/info明文交叉验证一致。scripts/build_pua_map.py—— 字体 ID 变化时重建映射表:下载字体 → 解包 TTF → 渲染字形对照图, 再由模型读图填表。
Scan to join WeChat group