第一次把“阅读”装进手机的人大概率会在半小时里经历三段情绪界面素得像个记事本书源导进去之后搜索栏突然活了然后就是一发不可收拾地往书架里塞书。这款开源的安卓阅读APP在圈子里已经活了六七年没有开屏广告没有会员弹窗没有“再看一章解锁下一章”的设计靠一群志愿者用业余时间维护。它本身一本书都不带真正让它变成“全网小说都能搜”的是书源——一份份写给不同站点的解析规则。2613个书源这个数字听着唬人但数量从来不是重点重点是你能不能把其中真正好用的那二三十个筛出来、养好、在它们失效的时候修回来。这篇就把我从第一次接触到现在长期自用的经验摊开讲它到底是怎么工作的、书源怎么导怎么筛、失效了怎么排查、TTS朗读怎么配、以及想往前走一步自己写源该从哪下手。1. 一个不带内容的开源阅读器凭什么能活这么久1.1 它本质上是个“壳”里面装的是规则很多人第一次用会困惑为什么装完打开是空的因为阅读APP的设计思路和普通阅读软件完全不同。普通阅读软件是“内容阅读器”打包卖给你书在服务器上你只是租了个入口而阅读APP是“阅读器规则引擎”它只负责把网页上的文字抓下来、排好版、翻页、记录进度至于内容从哪来取决于你导入了什么书源。用浏览器打个比方最直观浏览器自己不知道什么是“新闻”它只知道怎么把HTML渲染出来。书源就相当于一份写给某个具体网站的说明书告诉APP“搜索结果的列表在哪个标签里、书名取哪个属性、正文在哪个div、下一章链接长什么样”。规则一换同一个APP就能去读完全不同的站点这种“壳规则”的解耦设计才是它生命力的来源。顺带说一句它支持本地导入TXT、EPUB这类文件也支持RSS订阅源所以就算你一个网络书源都不导把它当成一个纯粹的本地阅读器追更工具也是完全成立的。1.2 开源这件事带来了三个很现实的好处第一个好处是没有广告。这话不是口号。闭源免费App要养活团队必然要靠广告、会员或者导流变现而这些动作最终都会落在你的使用体验上。开源项目没有营收压力也就没有人往里面塞广告SDK和推送通道。第二个好处是规则可以自己改。商业软件遇到某个网站改版你只能等官方更新开源阅读器遇到同样的情况你可以自己动手把选择器改掉五分钟解决不用看任何人脸色。第三个好处也是最容易被忽略的数据是透明的、可迁移的。你的书架、阅读进度、书源、TTS配置都能打包成压缩文件或通过WebDAV同步出去书源本身就是结构清晰的JSON。这意味着哪怕有一天这个项目彻底没人维护了你手上那套配置和规则文件还在换一个兼容的实现继续用就是了。闭源App给不了你这种底气。提示正因为配置全在本地换手机前务必先做一次备份。丢了书源列表再一个个找回来是很多人踩过的第一个大坑。1.3 什么样的人适合折腾什么样的人别碰适合的人同时追好几本书、经常遇到“这本在这个站断更了那本在另一个站有”的情况、想统一阅读体验字号、行距、背景、翻页动画一致、有听书需求、愿意花半小时研究一下规则的人。对这部分人来说一次配置长期受益回报率极高。不太适合的人只想装上就立刻能看、不愿意理解“书源”这个概念的人以及期待它自带内容的人。另外提一句这个生态目前以安卓端为主其他平台的使用体验差异较大选择前先确认自己的设备环境不要想当然。2. 书源不是资源包而是一份网页翻译规则2.1 一份书源JSON里到底写了什么把书源想象成一份“给某网站写的爬虫配置”就不难理解了。它主要包含两部分站点信息去哪抓、带什么请求头和解析规则抓到之后怎么从HTML里把字段抠出来。核心字段大致如下字段作用备注bookSourceUrl站点根地址同时作为书源的唯一标识改了就变成新源bookSourceName书源显示名建议带上站点特征方便排查bookSourceGroup分组用于筛选和排序强烈建议手动整理searchUrl搜索入口{{key}}是关键词占位符ruleSearch搜索结果解析书列表、书名、作者、详情页链接ruleBookInfo详情页解析封面、简介、最新章节、目录页地址ruleToc目录解析章节列表、章节名、章节链接、分页ruleContent正文解析正文内容、下一页链接、内容替换header请求头部分站点需要Referer或User-Agentenabled / enabledExplore是否启用、是否参与“发现”失效的先关掉别让它拖慢全局搜索看明白这张表你就明白为什么有的书源“能搜到但点进去是空白的”——搜索规则写对了详情或目录规则没写对链路在中间断掉了。2.2 搜索、详情、目录、正文四条链路缺一不可一次完整的阅读要经过四次解析搜索拿关键词去站点的搜索页从结果页里提取出书名、作者和详情页链接。详情进入详情页拿到简介、封面、以及目录页的真实地址。很多站点的详情页和目录页不是同一个URL这一步就是负责跳转的。目录解析章节列表拿到每一章的标题和链接。如果目录是分页的还要靠“下一页目录”规则把后面的章节接着抓。正文进入章节页把正文提取出来。如果正文分了多页靠“下一页正文”规则自动拼接。规则写法上有三种常见前缀分别对应三种解析方式CSS选择器和XPath适合传统的服务端渲染页面JSON解析适合那种接口直接返回数据的站点。举个最小化的例子{ bookSourceUrl: https://example.com, bookSourceName: 示例站, searchUrl: /search?q{{key}}, ruleSearch: { bookList: #result-list li, name: h3text, author: .authortext, bookUrl: ahref }, ruleToc: { chapterList: #chapter-list a, chapterName: text, chapterUrl: href }, ruleContent: { content: #contenthtml, nextContentUrl: a.nexthref } }text、href、html、src是取属性的固定后缀。实际写的时候绝大多数字段都可以留空走“默认规则”只在站点结构特殊的地方单独指定这样书源会短很多也更好维护。2.3 差别不在技术门槛而在调试方法同一个网站老手十分钟写出能用的源新手折腾两小时还在抓空气差距通常不在编程能力上而在有没有一套稳定的找元素方法。几个经验优先选带id的元素作为定位锚点因为它在一个页面里唯一且基本不会被改其次选语义化的类名比如chapter-list、content而不是col-md-8 mt-20这种一眼就是模板生成的工具类尽量别用:nth-child(3)这类位置选择器运营随手插一条广告位你的规则就废了。另外抓的时候一定要看原始HTML不能只看渲染后的画面。有些站点内容是用脚本拼出来的你右键检查元素看到的那段文字在源码里根本不存在这种情况就只能走接口或者用脚本处理硬套CSS选择器是徒劳的。3. 2613个书源到手先别急着全选导入3.1 三种导入方式各自适合什么场景书源导入常见三种方式各有各的用途方式操作适合场景网络导入粘贴一个订阅地址长期使用作者更新后你能一键同步二维码导入扫一张图手机上不方便复制长链接时本地文件/剪贴板导入导入JSON文件或粘贴内容一次性批量导入合集或者自己整理的精简列表“网络导入”背后对应的是订阅概念你把一个地址填进去APP定时去拉最新版本。这是长期维护最省事的方式但前提是你信任这个地址的来源因为它随时可以替换掉你本地已有的规则。批量合集类的JSON文件一般就是把成百上千个书源对象塞进一个数组里导入时注意看提示很多APP会问你是“合并”还是“覆盖”选错了会把你辛苦调好的分组全冲掉。注意全选导入两千多个书源之后直接搜索一次搜索可能要跑几十秒甚至更久因为它在挨个请求。先筛选再启用顺序不能反。3.2 导入后的第一件事做一次批量体检导入完成后的标准动作是批量校验。在书源管理里全选选择校验功能APP会逐个请求并把结果标记成可用、失效、超时几类。这个过程会消耗不少流量和时间我的建议是分批跑一次两百到三百个跑完立刻处理不要一键两千个然后放着不管——中途出错你都不知道断在哪一批。校验结果里我最看重的是响应时间。一个源哪怕当前可用但每次搜索都要等三秒以上它在你的日常使用里就是负资产。我的处理逻辑很粗暴校验失败的直接关掉响应时间明显偏慢的降权处理只把又快又稳的放进出搜索范围。3.3 分组、排序以及“搜到却打不开”的处理分组这件事刚开始觉得多余用久了会感谢自己。我的分组是最简的三层常驻每天在用、稳定、备用能用但慢或者章节有缺、待观察新导入还没验证的。搜索的时候只勾选常驻分组速度和准确率都会明显提升。排序上把响应快的往前放因为搜索结果是按书源顺序聚合的排在前面的源决定了你点进去的第一印象。至于“搜到却打不开”绝大多数是这么几种情况一是搜到的是同名不同书作者对不上二是目录能出但正文空白通常是正文规则失效或者被反爬拦了三是正文里混进了站点尾巴广告和推广语这时候可以用内容替换规则把固定字符串删掉或者在设置里开启通用的净化替换。这三条几乎覆盖了我遇到问题的九成。4. 书源集体变红的那天一次完整的失效排查记录4.1 先分清是“源坏了”还是“环境错了”有天早上我打开APP常驻分组里二十多个书源齐刷刷变红搜索全部超时。第一反应是“难道所有站点同时改版了”这概率低得离谱。于是按顺序排查先看网络本身。切换到浏览器打开其中一个站点能正常访问说明不是整体网络问题。再看手机时间——如果系统时间偏差过大HTTPS请求会因为证书校验失败被拒这是个特别隐蔽的坑我确实遇到过。接着检查APP的省电策略是不是把它限制了后台网络最后确认APP版本没有改动过什么设置。结论是所有源同时失效八成是你这边的问题单个源失效才是站点的问题。这个判断方向能帮你省掉大量无效排查。4.2 从搜索到正文的逐段定位法确定了是单个源的问题后就要沿着四条链路往回找。我习惯画一张对照表从现象直接推规则现象大概率出问题的规则搜索没有任何结果searchUrl 或 ruleSearch.bookList搜到书名但点进去空白ruleBookInfo 的目录页地址详情正常但目录为空ruleToc.chapterList目录有章节但点进去无正文ruleContent.content正文只有第一段ruleContent.nextContentUrl 分页规则目录只显示最新几十章ruleToc.nextTocUrl 目录分页规则定位的工具是APP自带的调试功能。把出问题的URL粘进去它会把原始返回内容展示出来你就能直观看到网站当前的真实结构和自己规则里写的选择器做对比。十次里有九次问题就赤裸裸地摆在那儿类名从list-item改成了chapter-item或者正文容器从div换成了article。4.3 改得回来的几种典型情况站点改版导致选择器失效这是最常见的直接改成新选择器就行。稍微麻烦一点的是这几种加了请求头校验。有些站点会检查来源页不带Referer直接访问正文会被拒。解决办法是在书源的请求头里补上Referer指向站点主域。目录改成了动态加载。页面源码里只有一个空容器章节是后来用脚本填进去的。这时候要么找它背后的接口直接返回JSON要么用脚本来请求硬啃HTML是死路。正文做了分页。一章被切成三段每一段底部有个“下一页”。只要把下一页的链接规则填进正文规则里APP就会自动往下抓直到没有下一页。内容做了混淆。偶尔会碰到正文里插入随机字符、或者用图片替代部分文字的情况。前者可以用正则替换规则批量清理后者基本只能放弃这个源性价比不高。这里有个心态上的经验分享不要试图修好每一个源。留三十个稳定的源比留五百个半死不活的源有用得多。修一个被反爬重度保护的站点花掉的时间足够你写完三个新源了。5. 把阅读APP当听书机TTS引擎怎么选怎么调5.1 系统引擎、本地引擎、自建接口三条路线朗读功能是阅读APP被严重低估的一块。它的原理是把当前章节的文本切片交给语音引擎合成音频边合成边播放。常用的引擎可以分三类路线特点适合谁系统内置TTS免配置音色一般依赖系统自带语音包图省事、偶发使用本地离线语音引擎需自行安装语音包离线可用隐私好通勤、流量敏感、注重隐私自建HTTP接口引擎音色最自然需要自己搭服务、填接口模板听书重度用户自建接口这类引擎的配置方式通常是填一个URL模板里面带占位符由APP把当前要朗读的文本和语速替换进去请求返回音频数据流。不同版本支持的占位符名称不完全一样配置前一定看一下应用内关于朗读引擎的说明页照抄别人的模板有时候会因为版本差异不生效。注意使用任何在线语音服务前先确认它的使用条款允许你的用法和调用量级不要拿个人接口去跑整本书的批量合成。5.2 参数调试语速、分段、停顿的实际手感听书和看书是两种完全不同的信息接收节奏参数调不好会很折磨人。我的实测心得语速。中文TTS的默认语速普遍偏快听起来像赶时间。往低调一档到两档反而更容易长时间听下去。另外有个反直觉的点语速调慢之后长句的断句错误率会下降因为引擎有更多时间处理。分段长度。这是最容易被忽略的参数。整段几百字不分切地丢给引擎很容易出现后段被截断、或者合成失败的情况。把单次送入的文本长度限制在一个较小的值让APP多切几刀稳定性会大幅提升。朗读替换。多音字是中文TTS的永恒痛点“重”“行”“还”“长”这类字几乎必然念错几个。APP里一般有朗读替换规则可以把高频误读的词替换成同音字或者干脆在朗读时跳过某些符号。我的做法是听的时候随手记听到错的就加一条替换两周之后基本就顺手了。自动翻页与定时。开启朗读时自动翻页配合定时停止就是完整的睡前听书场景。锁屏朗读也需要在系统权限里给它放行否则锁屏几分钟就被杀掉了。5.3 听书场景下最容易踩的几个坑后台被杀。国产系统的省电策略普遍激进锁屏后几分钟APP的网络和音频线程就被冻结。要做的动作是在系统设置里把它加入电池优化白名单并且允许后台运行、允许自启动而不是在应用里反复点。这个坑我卡了很久才找到原因。蓝牙断连后不恢复。耳机断开重连之后朗读有时候会停在那儿不动需要手动点一下暂停再播放。这个属于音频焦点的问题没太多办法习惯就好。长章节被截断。表现是听到某一句话突然跳到下一章。原因基本是分段设置过大或者引擎返回长度有限制把分段调小就能解决。在线引擎被限流。高频调用之后突然全部失败通常是被限制了。这时候切回本地引擎过渡一下别硬刚。隐私。自定义引擎的接口地址里如果带了密钥分享截图或者发帖时记得打码。书源同理里面如果塞了你自己账号的Cookie分享出去等于把账号送人。6. 从伸手党到贡献者自己写源、维护源、参与开源6.1 写一个最小可用书源的完整过程从零写一个书源流程其实很固定我把它拆成六步确定目标站点并测试搜索。在浏览器里用关键词搜一次拿到完整的搜索URL把关键词部分替换成{{key}}这就是你的搜索入口。打开调试工具找到结果列表的容器。从最外层的列表元素入手确保这个选择器能一次性选中所有结果条目。写搜索规则。书名、作者、详情页链接各一行先只写这三个其他的留空走默认。测试搜索。在编辑界面上点测试看输出结果是不是完整的书列表。不通就回到上一步改。写目录和正文规则。目录注意区分是否需要分页正文注意是否需要翻页。保存并实跑一遍。用搜索→加入书架→打开目录→翻三章的完整链路验证而不是只在测试界面里点点。整个过程如果站点结构规整二十分钟能拿下一个。真正费时间的不是写而是遇到各种意外结构时的判断和取舍。6.2 订阅与分享的边界这里必须说清楚一件事书源的强大同时也意味着责任。我个人一直坚持几条原则优先用公共版权作品、正规渠道提供的内容和自己购买的内容分享书源文件之前先把里面可能残留的请求头、Cookie、账号信息全部清干净不要拿别人的站点去做高频压力测试调试的时候收敛一点。再补一条实操层面的订阅链接不要乱填。订阅意味着对方可以随时替换你本地的书源规则一个来路不明的订阅地址既可能带来一堆垃圾源也可能带来你不想要的东西。要么用自己整理的地址要么用长期可信的来源。6.3 低门槛参与开源项目的几个方向如果你用了一段时间觉得这东西不错想回馈一点什么其实门槛比想象中低得多提一个高质量的issue。带版本号、带复现步骤、带日志、带截图。一个信息完整的反馈比十条“用不了”有价值。帮忙整理文档。这类项目的文档往往滞后于功能把新版界面截图补上、把过时的说明改掉是实打实的贡献。反馈规则问题。发现某个规则在某些结构下会出错把最小复现样例整理出来提上去。写教程。你踩过的坑就是下一个人最需要的东西。这类内容不需要多专业需要的是具体。我自己现在的状态是手机里常年只留二十来个经过筛选的书源分成两组TTS用本地引擎通勤听、自建接口在家听每个月挑一个周末做一次书源校验和备份。这套流程跑下来日常使用几乎不会被打断。真正花时间的只有最初那两周——把规则摸清楚之后剩下的全是收益。如果你刚上手建议别一上来就贪多先挑五个源调通全链路把调试工具和替换规则的用法吃透后面再扩量就快了。