「搜索无结果」这五个字,大概是「阅读」APP新用户撞上的第一堵墙。装好客户端,兴致勃勃点开搜索框,输入书名,回车,屏幕上干干净净一行“未找到相关书籍”。很多人到这一步第一反应是软件有问题、版本不对、手机不兼容,来回折腾半小时,最后放弃。实际情况往往是:这个客户端本身是个空壳,它不自带任何内容,所有能搜到、能看的东西,全靠你喂给它的书源。书源列表是空的,或者里面躺着的那几个早就变成了“僵尸源”,搜索结果自然一片空白。
这篇东西就是把这个环节彻底讲清楚。书源到底是什么、为什么有人配22个、这22个该怎么挑、怎么导入、导入完不生效怎么办、后面失效了怎么排查,一条线走完。整个过程手速快的确实三分钟能搞定,但那三分钟背后有一堆细节,不然你导进去的22个书源可能只有5个能打。适合两类人看:一类是刚上手、连书源文件长什么样都没见过的新手;另一类是用了一段时间、书源列表越攒越多但越来越乱、天天手动清理的老用户。我会尽量把每个参数为什么这么填、每个步骤为什么这么做讲透,而不是甩一串链接让你自己悟。
1. 先把“书源”这个概念掰开揉碎
1.1 书源的本质是一份网站结构说明书
很多人把书源想象成“资源包”“书库压缩包”,这是最大的认知偏差。书源本质上是一段 JSON 格式的规则描述,它告诉客户端:去哪个域名搜索、搜索结果的列表在页面的哪个位置、书名和作者从哪个标签里取、点进详情页后目录怎么翻页、正文段落用什么选择器抓。它不含任何一本书的内容,也不含任何图片文件,纯纯的一份“操作说明书”。
打个比方,客户端像一台万能遥控器,书源就是一张张红外码表。码表本身不发信号,但它告诉遥控器按哪个频段、用什么编码去控制那台电视。码表写错了,遥控器按破也没反应;电视换了型号,码表也得跟着换。这就解释了为什么书源会“失效”——不是文件坏了,是它描述的那个网站改版了、换域名了、把搜索页从 GET 改成 POST 了,说明书对不上了。
理解这一点,后面所有操作都会顺很多。你不会再问“为什么导入成功了还是搜不到”,因为导入成功只代表 JSON 语法没写错,不代表规则还能匹配上目标网站。
1.2 22个书源这个数字,是怎么拍出来的
市面上流传的“22个书源”并不是什么官方标准,也不是某个神秘数字。它是长期实践里沉淀出来的一个经验值,背后其实是两个考量:覆盖率和冗余度。
单个书源的覆盖率是很有限的。有的站偏出版类,有的站偏网络长篇,有的站只收完本,有的站更新快但错字多。你只挂三五个源,搜冷门书大概率空手而归。而源挂太多也有代价:每次搜索都是并发请求,源越多,等待时间越长、耗电越多、被单站限流的概率也越高。22个左右是一个比较舒服的平衡点——足够覆盖大部分常见书名,又不会让搜索转圈转到你想砸手机。
冗余度是另一半原因。书源的生命周期普遍不长,今天好用下个月可能就挂了。如果你只有3个源,挂掉2个就直接残废;有22个,挂掉5个还有17个兜底。所以配置书源的正确心态不是“找22个最好的”,而是“找22个不同类型的,让它们互相备份”。
提示:不要迷信任何一份“永久有效书源合集”。任何合集都有保质期,通常以周为单位计算。真正靠谱的做法是学会自己判断和替换,而不是反复找新合集。
1.3 “搜索无结果”的六种真实原因
在动手之前,先把失败原因列清楚,省得后面瞎猜。按出现频率从高到低排:
| 排序 | 原因 | 典型表现 | 处理方向 |
|---|---|---|---|
| 1 | 书源列表为空或全部未启用 | 搜索秒回“无结果”,无加载过程 | 导入并勾选启用 |
| 2 | 书源规则失效(站点改版/换域名) | 单个源搜索超时或报错,其他源正常 | 更新或删除该源 |
| 3 | 搜索关键词编码问题 | 中文搜不到,英文或数字能搜到 | 检查搜索 URL 的编码占位 |
| 4 | 并发数与超时设置不合理 | 搜索转很久,最后只返回零星结果 | 调低并发、拉长超时 |
| 5 | 网络环境问题 | 所有源统一超时 | 检查网络连通性 |
| 6 | 书名本身太冷门 | 大部分源无结果,少数有 | 换关键词或换源类型 |
这六条里,第1条占了新用户问题的绝大多数。所以别急着怀疑软件,先去看书源管理页面里到底有没有东西、开关是不是灰的。
2. 导入前的准备工作,别跳过
2.1 客户端版本与入口位置确认
「阅读」这类客户端迭代很快,不同版本的菜单位置会有差异,但核心路径是一致的:底部导航或侧边栏 → 书源管理 → 右上角菜单 → 导入。有的版本把它放在“我的 → 书源管理”,有的放在“设置 → 书源”,找不到的时候直接在设置里搜“书源”两个字,比一层层翻要快。
版本选择上有一个原则:不追新,也不要用太老的。太老的版本可能不支持新版书源里的某些字段(比如规则里用了新的选择器语法),导入后字段被静默丢弃,表现为“导入成功但某个功能不工作”,非常难查。太新的测试版可能引入兼容性问题。稳妥的做法是用一个近期稳定的正式版,装好之后就不要频繁升级,除非你遇到了明确的、已知被修复的 bug。
注意:升级客户端前先导出一次书源备份。有些版本升级会重置部分配置,虽然不常见,但踩一次就够你重新配半小时。
2.2 书源文件的四种存在形态
拿到手的书源,形态可能完全不一样,处理方式也不同:
- JSON 数组文件(后缀 .json,内容以
[开头、]结尾,里面是多个对象):最常见的整包形态,一条导入指令能进来几十个源。 - 单条 JSON 对象(以
{开头):通常是从某个源里单独摘出来的,用来替换失效的某一个。 - 网络链接:给你一个地址,客户端自己去拉取。方便,但依赖那个地址长期有效。
- 二维码或纯文本:本质还是上面几种,只是传递方式不同,扫码或粘贴即可。
判断形态很快:把文件用文本编辑器打开看一眼首字符,[是数组,{是单条。搞错了会导入失败,报“格式错误”。
2.3 一个必须先说清楚的合规前提
技术上讲,书源机制是一套通用的网页数据结构解析方案,本身中立。但用它去抓什么内容,是有边界的。优先选择公版作品、明确授权的内容、或者你自己搭建或获得许可的来源,这是底线。商业平台的付费内容、需要登录才能看的章节,不要通过自制规则去绕。这不是技术能不能做到的问题,是该不该做的问题。后面讲的所有调试技巧,前提都是你对自己抓取的来源有正当使用权。
我个人的做法是:书源列表里始终保留几个公版书库类型的源作为基本盘,其余按需增补。这样即使某一批源集体失效,也不至于完全断粮。
3. 三分钟导入实操:三条路径任选
3.1 本地文件导入,最稳的一条路
这是成功率和可控性最高的方式,尤其是当来源给的是一份整包 JSON 时。
第一步,把.json文件放进手机存储。位置随意,但建议固定一个目录,比如手机根目录下建一个bookSource文件夹,以后所有书源文件都往这里扔,找起来不用翻半天。用数据线、聊天软件传文件助手、网盘下载都行,注意别放在应用私有目录里,客户端读不到。
第二步,打开客户端,进入书源管理,点右上角菜单,选本地导入。系统会弹出文件选择器,定位到你刚才那个目录,选中文件。有些版本支持一次多选,可以连着导几份。
第三步,导入后通常会弹出一个列表,展示这批书源的名字和 URL,每条前面有勾选框。这里有个小细节:默认可能是全选,也可能全不选,不同版本行为不一致,一定要亲眼确认。确认后点确定,等待写入。
第四步,回到书源管理主界面,检查列表里是不是真的多出来了。重点看两点:条目数量对不对得上文件里的对象数;每条右边的启用开关是不是打开的。有版本导入后默认是关闭状态,必须手动全选启用,否则搜半天还是空。
实操心得:一次导入数量控制在 50 条以内。整包里塞几百条的情况很常见,一次性全导进去会让列表管理变得极其痛苦,而且大部分你根本用不上。分批导入、边用边筛,效率反而高。
3.2 网络链接导入,图省事但要看运气
菜单里选网络导入,把地址粘进去,确认。客户端会自己去请求并解析。
这条路的优点是省了传文件的步骤,缺点是失败原因排查起来更麻烦。常见失败有三种:链接指向的内容不是合法 JSON(比如返回了一个网页错误页)、链接需要额外的请求头才能访问、链接已经失效返回 404。遇到失败,最快的验证方法是用浏览器打开那个链接,看返回的到底是什么。如果浏览器里显示的是一堆正常的 JSON 文本,那就是客户端那边的问题;如果浏览器里是错误页或者空白,别折腾了,换来源。
3.3 剪贴板与扫码导入,适合零星补充
单条书源用剪贴板最方便:复制那段 JSON 文本,进书源管理,菜单里选剪贴板导入,客户端自动读取剪贴板内容并解析。注意复制的时候要把完整的大括号配对复制进去,少一个}就会解析失败。
二维码导入本质上是把链接编码成了图形,扫码后走的是网络导入的逻辑,适用场景是你想从另一台设备把配置挪过来,或者来源只提供了二维码。
3.4 导入后的三分钟验收,别省
导入完成不等于配好了。花三分钟做下面三件事,能省掉后面一小时的困惑:
第一,搜一本你确定存在的公版书。用一个非常常见的书名做测试,比如四大名著的任意一本。如果所有源都搜不到,说明启用状态或导入环节出了问题;如果部分源有结果、部分没结果,那是正常的源质量差异。
第二,点开一本书,走完“详情 → 目录 → 正文”三级页面。很多人只测了搜索就以为万事大吉,结果点进去发现目录空白、或者正文乱码。三级页面全通,才算这个源真正可用。
第三,看书源列表里的状态显示。多数客户端会在每条书源后面显示上次请求的耗时或错误标记。耗时几百毫秒的是健康源,超过十秒或者带红色标记的,基本可以判定为失效或半失效。
4. 书源 JSON 结构逐字段拆解
想真正搞懂书源,光会导入不够,得能看懂那份 JSON。这一节把核心字段拆开讲,看懂了,你就能自己判断一个源为什么坏、坏在哪。
4.1 基础身份字段
一份最小可用的书源大致长这样:
{ "bookSourceName": "示例书源A", "bookSourceUrl": "https://www.example.com", "bookSourceGroup": "公版书库", "bookSourceType": 0, "enabled": true, "searchUrl": "/search?q={{key}}&page={{page}}", "ruleSearch": { "bookList": "div.result-list > div.item", "name": "h3 > a@text", "author": "span.author@text", "bookUrl": "h3 > a@href", "intro": "p.desc@text" }, "ruleBookInfo": { "name": "h1@text", "author": "div.info > span:nth-child(2)@text", "intro": "div.summary@text", "tocUrl": "" }, "ruleToc": { "chapterList": "ul.chapter > li > a", "chapterName": "text", "chapterUrl": "href" }, "ruleContent": { "content": "div#content@text" } }几个关键点:
bookSourceName是显示名,随便起但要能让你一眼分辨,别全叫“精品书源1、2、3”,后面根本不知道哪个是哪个。bookSourceUrl是这个源的根域名,它有两个作用:一是作为唯一标识去重,二是作为相对链接的拼接基准。这个字段写错,后面全错。bookSourceGroup是分组,用来在列表里折叠归类,后面的 5.1 会专门讲怎么用。enabled是启用开关,导入后如果大批量是 false,搜索自然没结果。
searchUrl里的{{key}}是关键词占位,{{page}}是页码占位。有些源的关键词还需要额外编码,会写成类似{{java.encodeURI(key)}}的形式,目的是把中文转成 URL 百分号编码。这是中文搜不到、英文能搜到问题的头号元凶,看到这种症状先查这里。
4.2 搜索规则的匹配逻辑
ruleSearch下的bookList是“结果条目的容器选择器”,它决定了客户端怎么把一页搜索结果切成一条条记录。这一条写错,后面所有字段都取不到值。
举个例子:页面结构是
<div class="result-list"> <div class="item"><h3><a href="/book/1">书名甲</a></h3><span class="author">作者甲</span></div> <div class="item"><h3><a href="/book/2">书名乙</a></h3><span class="author">作者乙</span></div> </div>那么bookList就应该写成div.result-list > div.item,意思是“在 result-list 下找所有 item”。写成div.item也能跑,但如果页面上别的地方也有 class 为 item 的元素,就会混进脏数据。层级写得越精确,取到的数据越干净,这是排查“搜索结果里混进了奇怪条目”的第一手段。
后面的name、author、bookUrl都是相对于单条记录的相对选择器。@text表示取文本,@href表示取链接属性。bookUrl特别重要,它可以是完整 URL,也可以是相对路径,客户端会用bookSourceUrl去拼。如果取到的是相对路径但没拼对,表现为“能搜到但点进去 404”。
4.3 详情、目录、正文三段规则
搜索只是入口,真正决定能不能看的是后面三段。
ruleBookInfo负责详情页。name、author、intro、coverUrl这些比较直观。tocUrl这个字段容易被忽略:大多数情况下留空就行,客户端会用当前详情页地址当目录页;但有些网站的目录在单独的子页面上,这时候就必须在这里写死目录地址的规则,否则目录永远是空的。
ruleToc负责章节目录。chapterList是章节链接的容器,chapterName和chapterUrl是相对于单条章节的相对规则,写text和href就表示取该元素的文本和链接。这里最常见的坑是分页目录:有的站一本书的目录分成好几页,如果规则里没有处理翻页,你只能看到第一页的几十章,后面的全丢。判断方法很简单,看目录总章数跟你预期的是否差很多。
ruleContent只有一个核心字段content,但它是最容易出问题的一个。正文选择器写得太宽,会把广告、推荐位、“本章未完请点击下一页”一起抓进来;写得太窄,可能只抓到第一段。正文规则调优是整个书源维护里最花时间的部分,后面第6节会专门讲几种典型症状。
4.4 选择器语法速查
不同客户端支持的语法略有差异,但主流是这几种,认识它们你就能读懂绝大部分书源:
| 语法前缀 / 符号 | 含义 | 使用场景 |
|---|---|---|
@css:或无前缀 | CSS 选择器 | 绝大多数结构化页面 |
@XPath: | XPath 路径 | 结构复杂、需要按位置定位时 |
@json: | JSON 路径 | 目标站是接口返回的 JSON |
@text/@href/@src | 取文本 / 链接 / 图片地址 | 配合前三种使用 |
&& | 多个规则拼接 | 正文分多段容器时合并 |
|| | 多个规则依次尝试,取第一个成功的 | 兼容页面改版的容错写法 |
##正则##替换 | 正则清洗 | 去掉正文里的多余字符 |
||这个容错写法值得单独说一句。比如正文容器有时是div#content、有时是div.content,你可以写成div#content@text||div.content@text,先试第一个,失败了自动试第二个。这招在页面改版频繁的源上非常管用,能显著延长一个书源的寿命。
5. 22个书源怎么分组、怎么调、怎么养
5.1 分组不是装饰,是效率工具
导进22个源之后,如果不分组,列表就是一坨。bookSourceGroup字段或者客户端里的分组功能,就是用来解决这个的。我的分组习惯是四类:
- 主力组(3~5个):速度快、正文干净、更新及时,日常搜索优先用这几个。
- 补充组(8~10个):覆盖率好但速度一般,主力组搜不到时才轮到它们。
- 公版组(3~5个):内容偏经典作品,稳定性通常最好,作为长期基本盘。
- 待观察组:新导入还没验证过的,先扔这里,用一周没问题再往上提。
分组的好处不只是看着清爽。很多客户端支持按分组启用/停用和按分组调整搜索优先级,你可以一键把待观察组全关掉,搜索速度立刻回来。这比一条条去点开关省事得多。
5.2 并发数与超时,这两个参数决定体验
这两个参数是新手最容易忽略、但对体验影响最大的。
并发数指的是同时向多少个书源发起搜索请求。设得太高,比如50,22个源一起冲出去,本地网络瞬间打满,每个请求的平均响应时间反而变长,还容易被目标站判定为异常流量直接拒绝。设得太低,比如1,那就是排队一个个来,22个源挨个超时,转两分钟都出不来结果。
经验值:并发数控制在 8~16 之间,22个源分两批跑完,整体耗时通常在几秒内。可以自己试:从8开始往上加,加到搜索开始变慢或者失败率上升,就退回上一档。
超时时间指的是单个请求最多等多久。设太短,网络稍慢一点就误判为失败;设太长,一个挂掉的源能耗掉你十几秒。建议8~15 秒。判断依据是看你那些健康源的平均响应耗时,超时时间设成平均值的3倍左右比较合理。
提示:这两个参数没有通用最优解,跟你的网络环境、书源质量、使用时段都有关。调参的正确姿势是改一次、搜同一本书、记下耗时和成功数,对比着调,别凭感觉。
5.3 定期体检,比不断找新源更重要
书源会腐烂,这是必然的,不用对抗,只要管理。我给自己定的节奏是两周一次体检:
先在书源管理里按“上次更新时间”或者手动翻一遍,重点看那些带错误标记的。然后随便搜三五个常见书名,观察哪些源从来没出现过。连续两次体检都没贡献过结果的源,直接删掉——留着只会拖慢搜索。
删的时候有个技巧:先停用观察一周再删,别一上来就删。有些源是间歇性的,特定时段抽风,平时是好的。停用一周后如果还是没需求,再删不迟。
另外,替换失效源的优先级应该是:先找同类型站点的源补位,再考虑调整现有源的规则。很多时候一个源“失效”只是某个字段的选择器过期了,改一行就能救活,比找个新的重新配置要快得多。
6. 常见问题排查实录
6.1 症状对照速查表
| 症状 | 最可能的原因 | 第一步怎么查 |
|---|---|---|
| 所有书搜不到,秒回无结果 | 书源未启用 / 列表为空 | 看书源管理里的开关 |
| 部分源无结果,部分正常 | 那些源失效了 | 单独搜一次,看是否超时 |
| 中文搜不到,英文数字能搜 | 关键词未做 URL 编码 | 检查 searchUrl 的占位写法 |
| 能搜到,点进去空白 | 详情页规则失效或 bookUrl 相对路径拼错 | 手动在浏览器打开该书链接 |
| 详情正常,目录空白 | tocUrl 未指定或 chapterList 选择器错 | 检查目录页真实结构 |
| 目录只有前几十章 | 目录分页未处理 | 看目录页有没有“下一页” |
| 正文只有一段 | content 选择器范围太窄 | 用&&合并多个容器 |
| 正文混入广告和推荐语 | content 选择器范围太宽 | 用正则或##清洗 |
| 正文乱码 | 编码识别错误 | 检查源站字符集 |
| 导入报“格式错误” | JSON 语法不合法 | 用在线校验工具过一遍 |
6.2 三个真实排查案例的还原
案例一:导入成功但搜索永远为空。某次我导入一份整包源,提示成功,列表里也看到了条目,但搜索怎么都没结果。翻到书源管理里逐条看,发现所有条目的开关都是关闭状态——那份文件的每个对象里enabled字段都写的是false。手动全选启用后立刻正常。这个坑的解释是:导出的源如果来自一个“已停用”的列表,导出时会保留停用状态。导入后逐一确认启用状态,这一步永远不能省。
案例二:中文书名搜不到,输入拼音或英文却出结果。这个症状几乎可以百分百锁定在搜索 URL 的编码上。原始页面接受的是?q=书名这种形式,但实际请求必须把中文转成百分号编码,否则服务端接收到的是乱码。解决办法是给占位符加上编码处理,写成带encodeURI的形式。改完之后中文立刻能搜。
案例三:正文抓到一半就断,后面全是“本章未完”。这个不是规则坏了,是目标站把正文做了分页,一章拆成两三个页面,每页结尾放一句引导语。处理方式有两种:一是让规则识别翻页链接、自动请求下一页合并;二是在content规则里加正则,把结尾那句引导语清掉,再配合翻页。前者一劳永逸但要写规则,后者快但要一章章点。我一般先用后者救急,有空了再改前者。
6.3 几条踩出来的避坑经验
第一条,改规则前一定先备份。同一个源,改之前导出一份单条 JSON 存着,改坏了能回滚。我有一次手滑把一个主力源的正文选择器改错,忘了备份,只能凭记忆重写,折腾了四十分钟。
第二条,调试单个源的时候,把其他源全部停用。不然搜一次跑22个,你根本分不清结果到底来自哪个源,排查效率极低。单独启用一个、搜一次、看结果,这是最快的定位方法。
第三条,不要迷信“全自动书源更新”这类功能。自动更新确实省事,但更新源本身也是外部给的,质量和安全性无法保证。我见过自动更新把一批本来好好的源换成一批垃圾源的情况。手动更新慢一点,但你知道自己在吃什么。
第四条,书源数量和搜索体验是倒U型关系。刚开始加源,覆盖率上升,体验变好;加到某个点之后,无效源开始拖累整体,体验反而下降。找到那个拐点,比不断加源重要得多。我的拐点大概在25个左右,22个是留了余量的舒适区。
7. 进阶:自己动手维护一个书源
7.1 用浏览器开发者工具摸清页面结构
所有书源规则的本质,都是把网页结构翻译成选择器。所以会看网页结构,就等于会写规则。
流程是这样:在电脑浏览器里打开目标页面,按 F12 打开开发者工具,用元素选择工具(左上角那个小箭头)点一下你想抓的内容,右侧就会高亮对应的 HTML 节点。看它的标签名、class、id、层级关系,选择器就出来了。比如你点了正文,发现是<div id="content">,那content规则写div#content@text就行。
关键是要点列表里的第二个条目,而不是第一个,因为很多站会给第一条加特殊 class(比如first),拿它写规则会导致后面所有条目都失配。这是新手写规则最常见的翻车点。
7.2 CSS 和 XPath 怎么选
两种都能用,选择依据是页面结构复杂度。
CSS 选择器写起来短、可读性好,对付规整的列表和简单的层级关系足够用。比如div.list > ul > li > a这种,一眼能看出意图。
XPath 的优势是能按文本内容和位置关系定位,比如“包含‘下一章’三个字的那个链接”,CSS 做不到,XPath 可以写成//a[contains(text(),'下一章')]。遇到翻页链接、特殊按钮这类不好用结构定位的元素,XPath 更趁手。
我的习惯是:结构清晰的用 CSS,需要按内容或位置匹配的用 XPath,同一个源里混用完全没问题。
7.3 长期维护的几点真实体会
书源这事,本质上是一场持续的消耗战。你不可能配一次管一年,能做到的是把维护成本降到可接受的范围。
第一,主力源宁少勿多。我现在长期只维护5个主力源,其余全是备份。主力源一旦挂了我会立刻修,备份源挂了就挂着,等凑够一批再统一处理。这样日常维护时间能压到每周十分钟以内。
第二,记录每个源的适配要点。我在笔记里给每个主力源记一行:域名、正文容器的选择器、有没有分页、上次验证时间。别小看这一行,隔一个月回来修的时候,能省掉重新摸索的全部时间。
第三,遇到页面大改版,直接放弃比硬修划算。有些站改版是把整个前端框架换掉,所有选择器全废,修起来的成本接近重写。这种时候找一个同类型的新源,二十分钟搞定,比死磕两小时强。
第四,别把书源当成唯一依赖。真正重要的书,本地下载一份存着,比什么都靠谱。书源是“找书”的工具,不是“存书”的方案,这两件事别混。
最后分享一个我一直在用的小习惯:每次新增书源,我都会在名字后面加一个导入日期的后缀,比如“示例书源A-0315”。看起来有点强迫症,但当你列表里躺着二十多个源、需要判断哪个是三个月前的老古董时,这个后缀能让你三秒钟做出删留决定。书源管理这件事,省下来的从来不是配置的时间,是后面排查的时间。