1. 先确认边界:这套“下载与转换”方案到底能处理什么
先说个容易被骂的点:现在网上搜“番茄小说下载器”,跳出来的东西鱼龙混杂,很多号称“全网小说一键扒取”的脚本、工具,本质上都在踩版权红线。我自己也试过几款,最后无一例外都碰上了章节乱码、文件残缺、甚至账号风控的问题——你以为你在白嫖资源,其实是在拿自己的账号安全开玩笑。
所以这篇指南先划个清楚边界:我们处理的内容,只限于你自己在番茄小说App里已经加入书架、正常订阅或通过官方活动合法解锁的作品。对这些内容做个人离线备份、格式转换、笔记整理,属于个人合理使用范畴。整个流程不涉及越狱、root、抓包破解,也不用绕过任何会员机制。
那为什么还需要“下载器”这种东西?用过番茄小说的人都知道,官方App的离线缓存有几个老问题:缓存文件藏在应用私有目录里,换手机基本等于白缓存;导出的txt要么是乱码,要么章节排序莫名其妙;想在Kindle、墨水屏阅读器上看,官方根本不给你开口。更别提吐槽最多的“HTML格式转WPS表格”这种需求——你想把某个章节里的人物关系、伏笔线索整理成表格,手动复制怕是要复制到天亮。
这套方案的定位就是解决上面这些事:把你自己阅读库里的内容,用合法能用的手段拉到本地,再转换成txt、epub、html,甚至进一步加工成Excel/WPS表格里的结构化笔记。下面所有步骤我都实测过一遍,踩过的坑也都标出来了。
2. 从书架到本地:先把章节内容“物归原主”
2.1 官方离线缓存是第一步
很多人不知道,番茄小说App本身就给了一个“本地化”的口子:在书籍详情页或阅读页的目录里,有一个“离线缓存”按钮。把这个章节包下下来,你的书就脱离了需要实时联网的状态。这一步很关键——它是后续所有操作的“合法地基”,因为缓存文件是官方App自己生成的,你在做的只是把官方生成的东西找到并复制出来,而不是去逆向什么接口。
操作路径很简单,但有个细节很容易忽略:
- 打开一本书,进入目录页;
- 点击“离线缓存”或“下载全部章节”;
- 在设置里务必选“全程维持下载”并保证后台不被系统杀掉;
- 下载完成的标识,是每章标题右侧出现一个绿色或蓝色的下载图标。
缓存完成后,这本书就在你的手机里留下了一份完整的章节数据。接下来的问题只有一个:它到底存在哪?
2.2 不同平台的文件定位方法
这里的分歧比较大,我按平台给你列清楚:
- Android端:路径通常在
/Android/data/com.dragon.read/files/下的某个子目录里,具体名称每个版本不完全一样。你会发现里面是一堆没有扩展名的文件,或者是一串哈希命名的目录。不用慌,看到文件大小在几KB到几百KB之间、匹配你章节数的,基本就是缓存内容。有些版本会用.dat或.bin结尾,这些不是加密,只是没给正确扩展名而已。 - iOS端:由于沙盒机制,普通用户基本碰不到应用私有目录。但番茄小说在设置里提供了“导入/导出”相关的入口(不同版本位置不太一样),可以通过系统文件App访问“我的iPhone”里的番茄小说文件夹。如果你只想备份,可以用这种方式把整个文件夹拷出来。
- PC模拟器:如果你在电脑上装了安卓模拟器(比如MuMu、雷电),缓存文件的定位和Android端一致,而且操作起来更方便——直接用文件管理器就能拖出来。强烈建议新手从这条路走,省去手机文件权限的折腾。
我自己的习惯是:先用模拟器把整本书缓存完,再用文件管理器批量导出。实测一本书300章,大概占用20-40MB空间,导出的过程非常顺滑。手机端不是不行,但Android 11以上的分区存储限制会导致你在文件管理器里看不到/Android/data下的内容,需要用系统自带的“文件”App或者插数据线用电脑操作,麻烦不少。
2.3 缓存文件为什么是“没扩展名的乱码”?
第一次导出的人,十有八九会被吓到:文件名是32位哈希,打开后是一堆夹杂着HTML标签的文本,或者干脆是二进制。这其实不是加密,而是番茄小说缓存机制的特点——很多版本会把章节内容包装成「带标识头的伪HTML文件」或「XML结构数据」,中间还混着一些JSON用于记录章节信息。
判断方法很简单:用文本编辑器(比如VS Code、Notepad++)打开一个文件,如果看到里面有一行行的<p>标签或者chapterName之类的字段,那它就是“披着乱码外衣”的常规文本;如果全是NUL字节和不可读符号,那才是真压缩或者加密(这种情况请直接放弃,别跟它较劲)。
对我实测过的多个版本来说,绝大多数情况属于前者。这就意味着你不需要做任何破解,只需要把有效内容提取出来,重新整理成通用格式就行了。
3. 转换链路设计:为什么不要直接“另存为txt”
拿到缓存文件后,下一步是格式转换。但这里有个新手最容易犯的错:直接用文本编辑器打开,全选复制,粘贴到一个新txt文件里。结果就是——每章开头多了一堆元数据,段落间距全乱,换行符有\n有\r\n,在Kindle上看简直是灾难。
正确的做法是先想清楚你的目标格式,然后设计一条“中间格式”链路。我个人推荐的链路是:
缓存文件 → HTML中间文件 → 目标格式(txt/epub/表格)为什么要以HTML作为中间格式?三个原因:
- 结构保留最完整。缓存文件里原本就有段落标签和标题信息,转成HTML后这些结构不会丢失,后续转epub时能自动识别章节目录。
- 清洗污染数据容易。那些混在正文里的JSON、时间戳、设备号,在HTML里用正则批量替换就好操作得多。
- 后续玩法多。HTML可以继续转成PDF、Word、甚至导入WPS表格做二次加工,兼容性最好。
确定了链路之后,工具选型就顺理成章了。我的工具列表如下(全部免费,跨平台):
- Calibre:电子书管理的瑞士军刀,主用于HTML转epub/txt,以及最后的书库整理。
- Pandoc:命令行转换神器,擅长各种格式互转,尤其适合批量处理多文件。
- Python + BeautifulSoup:负责最脏最累的“清洗”工作,把每个缓存文件里多余的杂质剔除掉。
- WPS Office:最后把HTML内容粘贴成结构化表格的阵地。
这套组合的好处是:每一环都能独立验证效果,出了问题也知道是哪一步干的。下面我直接给你实操步骤,每一步都是能抄作业的级别。
4. 实战演示:三种最常用的批量格式转换方法
4.1 方法一:用Python脚本清洗缓存文件
缓存文件命名没有规律,清洗逻辑却非常统一。这里我放一个我每周验证过的脚本思路,你可以按自己的缓存结构微调:
import os import re from bs4 import BeautifulSoup input_dir = "./cache_files" output_dir = "./cleaned_html" os.makedirs(output_dir, exist_ok=True) for fname in os.listdir(input_dir): file_path = os.path.join(input_dir, fname) with open(file_path, "r", encoding="utf-8", errors="ignore") as f: raw = f.read() # 提取正文主体(按实际缓存结构调整选择器) soup = BeautifulSoup(raw, "html.parser") content_div = soup.find("div", class_="chapter-content") or soup.find("article") if content_div is None: # 若没有明显的结构化标记,尝试用正则截取正文起止位置 m = re.search(r"<p>(.*?)</p>", raw, re.S) if m: content_div = BeautifulSoup(f"<div>{m.group(0)}</div>", "html.parser") else: continue # 清洗残余的JS变量、隐藏字符、多余空白 text = content_div.get_text("\n") text = re.sub(r"\n{3,}", "\n\n", text).strip() # 写出HTML片段(保留p标签,方便后续转换) clean_html = f"<html><head><meta charset='utf-8'></head><body><h1>{fname}</h1>{content_div.prettify()}</body></html>" out_path = os.path.join(output_dir, fname + ".html") with open(out_path, "w", encoding="utf-8") as f: f.write(clean_html) print("清洗完成,输出目录:", output_dir)这段逻辑并不复杂,重点在于两点。第一,解析时用errors="ignore"容错,避免某个缓存文件里有坏字节导致整个脚本崩溃;第二,输出时保留了<h1>标题和<p>标签,这样后续用Calibre导入时,章节结构会自动识别出来,不用手动调整。
如果你不想写Python,也可以用Sublime Text或VS Code里的正则替换,把那些JSON字段和HTML注释批量删掉。但说实话,超过100章的正则替换会很痛苦,脚本一次性跑完不香吗?
4.2 方法二:Calibre批量入库并导出epub
清洗完成后,打开Calibre,“添加书籍”,把刚才生成的HTML文件全部选中拖进去。Calibre会自动把每个HTML识别为一个单独的电子书文件,这是对的——因为每个HTML对应一章。但如果306章变成306本“书”,书库里就乱套了。
所以这里要分两步:先合并再转换。
- 在Calibre书库里全选所有章节(Ctrl+A);
- 右键 → 合并到一本书 → 合并书籍文件到一个新文件(注意勾选“按文件名排序”,如果你的文件名没有章节序号,就要在合并前先统一命名成
001.html、002.html这种格式); - 合并完成后,右键新生成的这本书 → 转换书籍 → 格式选epub;
- 在“外观”选项卡里,把“输入章节识别表达式”改成
<h1>标签对应的匹配规则(默认是正则//h[1-6],一般不用改)。
这一步转换出来的epub,在Kindle App、Apple Books、微信读书里都能正常打开,目录可跳转,排版比原版缓存清爽太多。
4.3 方法三:Pandoc一行命令搞定txt/markdown
Calibre适合图形化操作,但如果你的需求只是“导出一个干净的txt”“拿去做语音朗读”或者“导入笔记软件”,那Pandoc的效率更高。
在命令行里进入清洗好的HTML目录,执行:
pandoc 001.html 002.html 003.html -o novel.txt或者把所有HTML文件用通配符批量合并:
cat *.html | pandoc -f html -t plain -o novel.txt这个命令把多个HTML拼接成单一txt,-t plain会丢掉所有格式,只留纯文本,特别适合生成朗读版。如果你想要Markdown格式,只需要把-t plain改成-t markdown,后续导入Obsidian、Notion都非常方便。
需要注意:Pandoc默认对超大文件(比如300章合在一起)处理起来会吃不少内存,几百MB的中间文件可能卡一下,这是正常的。我建议分批处理,50章一组,最后再合并txt,速度会快不少。
5. 换个玩法:把小说内容做成WPS表格里的结构化笔记
搜索热词里有个很特别的组合——“番茄小说下载器,html格式转换wps表格”。乍看有点无厘头,其实我懂这个需求背后的场景:你想在阅读时做人物关系图谱、伏笔追踪、章节时间线整理,而这些用纯文本根本搞不定,只有表格才能做得好。
就拿我自己举例。我平时看悬疑小说时,会记录每一章出现了哪些人物、死亡时间、线索物品。手抄太累,直接复制又乱。后来我用我们上面生成的HTML文件,走了一遍“HTML转WPS表格”的流程,效果出乎意料地好。
具体操作:
- 用WPS Office新建一个空白表格,菜单栏点击“数据” → “导入数据”,选择
清洗后的HTML文件; - WPS会弹出导入向导,在“文件类型”里选“HTML文档”,它能把HTML里的段落结构解析成行;
- 导入后,你会发现每一章的文字内容被放进同一列的不同行里,这时再用“分列”功能(数据 → 分列),按你设定的分隔符(比如中英文句号、逗号)把人物名、动作、时间拆到不同列;
- 更省事的办法是:写一个简单的Python脚本,把HTML里每段的开头几个字作为“章节标题列”,后面正文作为“内容列”,直接生成CSV,再用WPS打开CSV。这样生成一张「章节索引表」就非常轻松。
这里分享一个我常写的CSV生成脚本:
import csv import glob from bs4 import BeautifulSoup with open("novel_notes.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["章节", "正文内容", "人物", "备注"]) for html_file in sorted(glob.glob("./cleaned_html/*.html"))[:50]: with open(html_file, "r", encoding="utf-8") as fp: soup = BeautifulSoup(fp, "html.parser") title = soup.find("h1").get_text() if soup.find("h1") else html_file paragraphs = [p.get_text() for p in soup.find_all("p")] content = "\n".join(paragraphs) writer.writerow([title, content, "", ""]) print("已生成 novel_notes.csv")注意我用了utf-8-sig编码,这个编码会在文件头自动加上BOM,WPS直接双击打开CSV时就不会出现中文乱码。如果你只写了一行utf-8,打开后表头全是问号,这个坑我踩过不止一次。
有了这张表,你后面想怎么折腾都行:筛选某个人物的出场章节、统计每个章节的字数变化、甚至用WPS自带的数据透视表做人物出场频率分析,都比在文本文件里翻来翻去方便得多。“鼠鼠我啊,终于不用一章一章复制了”——这种感受,你应该能懂。
6. 踩坑实录:最让人崩溃的几个问题和对应的排查思路
做这个项目最花时间的不是转换本身,而是逐个解决意外情况。我把踩过的坑按“从高频到低频”整理成一个表,你遇到问题可以先对着查:
| 问题现象 | 根因 | 排查思路与解法 |
|---|---|---|
| 导出txt后大量“口口口” | 原缓存文件里部分字符编码不是UTF-8,可能是GBK或Unicode转义 | 先查看文件头前几行是否是\u开头的Unicode转义,用errors="replace"重新读取,或对整段做unicode_escape解码 |
| 章节顺序错乱 | 缓存文件名是哈希,无法从文件名判断章节顺序 | 打开文件,在HTML标签里找到chapterName或数字序号字段,用脚本读取并改名为001_书名.html,而不是直接依赖文件名 |
| 转换出来的epub没有目录 | 中间HTML里没有用<h1>标记章节标题 | 回到清洗脚本,把标题提取后用<h1>包裹,再重新转一次 |
| Calibre合并书籍后只显示第一个文件 | 文件排序不对导致后面的章节被覆盖 | 合并前把每个HTML都检查一遍<title>标签,确保各不相同;文件重命名时用三位补零的序号,比如007而不是7 |
| 手机本地缓存文件复制到电脑后打不开 | 文件权限复制不完整,或文件仍然处于“未完成下载”状态 | 回到App,确认整本书下载完成后再复制;Android上用adb pull代替鼠标拖拽 |
| CSV在WPS里打开乱码 | 写入CSV时用了utf-8而不是utf-8-sig | 重新生成CSV,编码改为utf-8-sig,问题直接解决 |
| 部分章节内容重复/缺失 | 缓存时网络抖动,导致某些章节只有半截正文 | 删除App里对应书籍的缓存,重新执行一次离线下载,再导出 |
除了表格里这些,再补一个最容易忽略的点:脚本里读文件时千万要加errors="ignore"或errors="replace"。缓存文件虽然大体上是文本,但偶尔会混进去一两个二进制字节,不加容错处理,Python跑到第两百章时直接抛UnicodeDecodeError,前面的成果全都白费,只能重跑。我一开始就是没加这个参数,半小时的脚本白跑了两回,血泪教训。
7. 给备份文件找一个“长久的家”
所有转换做完,别忘了最重要的一件事:给你的书库做版本管理。格式转换不是终局,你怎么管理这批文件才是能不能长期用好的关键。
我现在的做法是:每本书一个目录,里面分为source(缓存原始文件)、clean(清洗后的HTML)、output(最终生成的epub/txt/表格),再放一个README.md记录这本书的下载日期、章节数、处理脚本版本。这样哪怕过了半年想重新生成PDF,也不用再从头下载缓存。
另外一个建议是:不要把文件分散存在手机和电脑多个地方。我是用网盘同步整个书库目录,同时在本地保留一份冷备份。万一某天手机上的番茄小说缓存被App更新清掉了,本地和网盘的文件依然完好无损。
处理自己合法购买的电子书这件事,折腾过一轮之后你会明显感觉到,那些“好看的书”和“能随你怎么用的书”之间,差的不是版权意识,而是一套顺手的工作流。这套流程熟练下来,从拿到缓存到产出epub和表格,半小时内就能跑完,基本不费脑子。
如果你手里的书比较老、缓存格式和我遇到的版本不一样,清洗脚本可能要小改一下选择器。但思路是通用的:先把官方给你生成的东西拿到手,再清洗成干净的中间格式,最后按需转成你想用的样子。别去碰那些声称“一键扒全站”的旁门左道,自己动手处理自己的书,才是最稳妥、最长久的路子。