资讯动态

Python实现番茄小说TXT免费下载与永久更新

发布时间:2026/10/13 19:39:28 来源:尧图企业网站定制
简介面向需要批量获取番茄小说文本章节的编程学习者、爬虫入门者以及希望自动追更的读者这份资源提供了一套自动化下载方案解决手动复制效率低、更新追踪繁琐的问题。压缩包内共3个文件包含Python源码、可直接运行的exe程序以及txt书目信息整体大小15.47MB兼顾源码学习与开箱即用。已有7083人学习下载。源码覆盖requests网络请求、BeautifulSoup解析、章节遍历、异常处理等关键点并设计了多线程提速与定时更新机制exe封装让不熟悉Python的用户也能直接运行txt文件便于批量查看书目与章节对应关系。通过学习源码可以掌握简易爬虫的完整流程直接使用exe则可满足离线追更需求整体适合爬虫入门实战与工具型复用。1. 番茄小说txt免费下载python永久更新为什么「自己写脚本」是最靠谱的路线想找「番茄小说txt免费下载python永久更新」方案的人通常不是没搜过工具而是已经试过好几个打包好的下载器有的下载到一半就停更有的作者直接不维护了有的导出的TXT里混着广告段和乱码。我自己当年也追更过某部小说每天手动把免费章节复制到本地TXT坚持了一周就放弃了。后来花一个晚上写了套Python脚本让它每天固定时间自动跑一次检测到新章节就追加到同一个文件里从此再没手动复制过。这篇笔记把整套思路、最小代码和踩坑记录完整写下来适合有点Python基础、想为追更或批量整理免费章节做自动化的人。先说清楚这个下载器由哪几部分组成再给能直接改着用的脚本最后重点讲增量更新、定时任务和五个我实际踩过的坑。需要提前说明这套方案只处理站点上免费公开、允许正常阅读的章节用于个人收藏或学习自动化不涉及付费内容的获取。2. 先拆清楚一个能“永久更新”的TXT下载器到底是什么结构我第一次写这个脚本时脑子里的模型特别简单拿到目录页提取所有章节链接循环请求正文页写入TXT。第一天跑得很顺利第二天再跑TXT里重复了所有章节。原因很直接——脚本没有「记忆」它不知道哪些章节已经下载过了。从那时候起我才意识到所谓「永久更新」不是一个炫技词它意味着脚本要解决三件事识别新章节、增量写入、定时触发。这三件事分别对应三个组件下载记录文件、追加写入逻辑、系统级任务计划。2.1 为什么是Python加requests加BeautifulSoup而不是更重的框架常见做法是Python配上requests和BeautifulSoup。理由很实际小说站的目录页和正文页基本都是服务端渲染的HTML拿到源码就能解析用不到浏览器自动化工具。Selenium这类方案要额外装浏览器驱动内存占用高速度也慢跑几十章就能感觉到差距。Scrapy功能强但单本小说用不上它的调度器、管道和中间件体系学习成本反而拖后腿。还有一层更现实的原因小说站的HTML结构会不定期改版BeautifulSoup的容错性好改版后通常只需要调整选择器不用换框架。requests负责网络请求设置超时和请求头BeautifulSoup负责从HTML里把章节标题和正文容器抠出来。这个组合足够处理绝大多数小说站也是我认为最值得优先尝试的选型。选型时可以这样判断如果目标页面返回的HTML里能直接看到正文文字就不需要浏览器自动化如果正文是JS动态渲染的再考虑更重的方案。我处理过的几个小说站都属于前者。另外不要一上来就上并发下载单线程加随机延时已经足够快而且不容易触发访问频率限制。小说文本量不大一本几百万字的书纯文本也就是几MB单线程跑完也就几分钟的事这不是性能瓶颈场景。2.2 先把页面的五个组成部分认清楚动手写代码前先花十分钟把页面结构看明白。用浏览器打开小说详情页按F12找到目录列表的HTML源码常见结构是某个div容器里包着一排a标签a标签的文字是章节名href是章节链接。正文页的结构更规律正文几乎都在一个固定的容器里比如div.chapter-content容器内是若干p标签和纯文本。把这五样东西确认好代码就成功了一半目录页URL、章节链接的补全规则、章节标题怎么取、正文容器选择器、本地TXT的格式。我第一次观察页面时用的是curl先把HTML落盘再慢慢看比在开发者工具里翻方便得多curl -s https://example-novel-site.com/book/12345/ \ -H User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ -o catalog.html curl -s https://example-novel-site.com/book/12345/chapter/1.html \ -H User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ -o chapter1.html命令的作用很简单把目录页和第一个正文页的HTML分别保存到本地文件。我不建议直接打印到终端因为HTML标签太多眼睛看不过来保存成文件后用编辑器搜索chapter-content这类特征class一下就能定位正文位置。参数方面User-Agent一定要带有些站点对没有UA的请求直接返回错误页-o指定输出文件名方便后续反复查看。把页面结构拆清楚后整个下载器的运行逻辑就是一条直线请求目录页、解析出章节列表、逐章请求正文页、解析正文、按顺序写入TXT。而「永久更新」则是在这条直线外加了三个辅助点一个记录已下载章节的文本文件、一个只追加不覆盖的写入模式、一个每天定时执行的任务计划。后面三章分别对应这三件事。3. 用Python把整本小说落成TXT下载器最小实现这章给一个能直接跑通的最小脚本。它不做增量更新也不做定时任务只解决「把整本小说完整下载成TXT」这个最基础的问题。先把这条主链路跑通后面再往上加更新逻辑排查问题时会简单很多。3.1 最小脚本从目录页到正文页到本地文件下面是完整脚本我习惯把每个环节拆成独立函数这样站点改版时只需要改对应函数不用动整体逻辑# -*- coding: utf-8 -*- import random import time import requests from bs4 import BeautifulSoup BASE_URL https://example-novel-site.com/book/12345/ HEADERS { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ), Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, } def fetch_html(url): resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding or resp.encoding return resp.text def parse_catalog(html): soup BeautifulSoup(html, html.parser) links soup.select(div.catalog-list a.chapter-link) chapters [] for a in links: title a.get_text(stripTrue) href a.get(href) if not title or not href: continue if href.startswith(/): href https://example-novel-site.com href chapters.append({title: title, url: href}) return chapters def parse_content(html): soup BeautifulSoup(html, html.parser) container soup.select_one(div.chapter-content) if container is None: return for tag in container.find_all([script, style, iframe]): tag.decompose() lines [line.strip() for line in container.get_text(\n).split(\n)] lines [line for line in lines if line] return \n.join(lines) def download_book(chapters, outputnovel.txt): with open(output, w, encodingutf-8) as f: for idx, chapter in enumerate(chapters, 1): html fetch_html(chapter[url]) content parse_content(html) if not content: print(f[跳过] 第{idx}章 {chapter[title]} 正文为空) continue f.write(f{chapter[title]}\n\n{content}\n\n) print(f[完成] {idx}/{len(chapters)} {chapter[title]}) time.sleep(random.uniform(1, 2)) if __name__ __main__: catalog_html fetch_html(BASE_URL) chapters parse_catalog(catalog_html) print(f共发现 {len(chapters)} 章) download_book(chapters)逻辑说明fetch_html负责拿到HTML源码并把响应编码设为页面实际使用的编码避免后续解析出现乱码parse_catalog从目录页HTML里提取所有章节链接遇到相对路径就补全成完整URLparse_content定位正文容器删掉script和style标签后提取纯文本download_book按章节顺序逐章下载并写入文件。整个流程是串行的每下载一章就写盘一次即使中途程序崩溃已经写完的部分仍然保留。参数说明timeout10表示请求超过10秒就放弃防止单章卡住拖死整个任务random.uniform(1, 2)是每章下载后随机等1到2秒作用是让请求间隔不规则降低被站点限制访问的概率。HEADERS里的User-Agent保持浏览器风格不要用默认的python-requests后者容易被识别为脚本请求。selector部分需要根据实际页面调整div.catalog-list和a.chapter-link是我处理过的一套典型结构换站点时先在浏览器里确认真实class名。3.2 正文清洗的边界哪些该删、哪些该留正文清洗是最容易翻车的地方。我的经验是分两层处理第一层删掉script、style、iframe这类对读者无意义的标签第二层过滤正文里混进来的导航文字。很多小说站在每章正文末尾会带上「上一章」「返回目录」「本章未完」之类的提示语这些不是小说内容要过滤掉。下面这段代码用关键词表逐行过滤NOISE_WORDS [上一章, 下一章, 返回目录, 本章未完, 请牢记本站域名] def clean_noise(lines): result [] for line in lines: if any(word in line for word in NOISE_WORDS): continue result.append(line) return result逻辑说明把parse_content提取出来的文本按行拆分只要某一行包含噪音词表中的任意一个词就把这一行整个丢弃。这里有个取舍关键词匹配是按行判断的如果正文某句话里恰好出现「下一章」三个字这一行会被误删。我实际遇到这种情况的概率很低导航短语和正常行文的搭配差异足够明显所以可以接受。如果你担心误删可以把匹配条件改严格一些比如整行完全等于「上一章」而不是包含。还有一个小习惯正文里的空行不要全部去掉保留段落间的换行会让TXT读起来更像正常文档。我的做法是先按换行拆分去掉每行首尾空白再丢弃空行最后用单个换行拼接。如果你喜欢每段之间空一行可以在拼接时改用两个换行看你个人阅读习惯。3.3 下载到一半断了能不能继续第一次跑下载脚本时我最担心的是网速波动和站点偶发错误。requests请求偶尔会超时如果不做处理整个脚本会直接抛出异常退出前面下载的章节虽然保住了但后面所有章节都得重跑。我习惯给请求加一个带重试的包装函数def fetch_html_with_retry(url, retries3): for attempt in range(retries): try: return fetch_html(url) except requests.RequestException as e: wait 2 ** attempt print(f第{attempt 1}次请求失败{wait}秒后重试{e}) time.sleep(wait) raise RuntimeError(f请求多次失败{url})逻辑说明每次请求失败后先等2的attempt次方秒再重试第一次等2秒第二次等4秒第三次等8秒。这个退避策略比固定等待更合理因为连续失败往往说明目标站点当前状态不理想短时间内重复请求只会加重问题。重试三次仍失败才抛出异常。参数说明retries3是重试次数上限可以按需调整wait 2 ** attempt就是指数退避的核心你也可以改成wait attempt * 3这种线性策略效果差不多。有了这个包装函数第3.1节脚本里的fetch_html调用可以替换成fetch_html_with_retry下载过程的容错性会明显提升。4. 让脚本做到「今天下载明天还能更新」增量追加与定时运行能完整下载整本书只解决了半个问题。真正让人省心的方案是脚本今天跑一遍明天再跑一遍只处理新出现的章节不重复下载旧内容。这就是标题里「永久更新」的落点。要实现它需要两个配合增量判断逻辑和系统定时任务。4.1 增量更新用下载记录文件判断新章节增量更新的核心是「记录」。我用一个纯文本文件当记录表每成功下载一章就把这章的URL追加进去。下次脚本启动时先把记录文件读成集合目录页解析出的章节只要URL在集合里就说明已经下载过跳过不在集合里的就是新章节下载并追加。下面是具体实现def load_record(record_filedownloaded.txt): try: with open(record_file, encodingutf-8) as f: return set(line.strip() for line in f if line.strip()) except FileNotFoundError: return set() def update_novel(chapters, novel_filenovel.txt, record_filedownloaded.txt): done load_record(record_file) new_items [c for c in chapters if c[url] not in done] if not new_items: print(没有新章节) return with open(novel_file, a, encodingutf-8) as nf, \ open(record_file, a, encodingutf-8) as rf: for c in new_items: html fetch_html_with_retry(c[url]) content parse_content(html) if not content: print(f[跳过] {c[title]} 正文为空) continue nf.write(f{c[title]}\n\n{content}\n\n) nf.flush() rf.write(c[url] \n) rf.flush() print(f[更新] {c[title]}) time.sleep(random.uniform(1, 2))逻辑说明update_novel接收目录页解析出的完整章节列表先读记录文件得到已下载URL集合再过滤出新章节列表。新章节为空就直接返回说明没有更新否则追加写入novel.txt同时把URL追加进downloaded.txt。注意写入用的是追加模式a不是覆盖模式w这样旧内容不会丢。参数说明我用章节URL作为记录键而不是章节标题因为标题可能重名。有些小说的章节名会重复出现比如「第X章 番外」这种标题去重会造成误判。URL在绝大多数站点是唯一的用它做键最可靠。flush的作用是强制把缓冲区内容写进磁盘防止脚本中途崩溃时丢数据这里每写完一章就flush一次牺牲一点性能换可靠性值得。4.2 定时任务怎么挂Windows任务计划与Linux cron两种写法增量逻辑写好之后剩下的问题就是「谁在每天帮我运行这个脚本」。常见做法是Windows任务计划程序或Linux的cron。Linux下最简单在终端执行crontab -e加一行# 每天 06:30 执行更新脚本日志追加到 run.log 30 6 * * * cd /home/me/novel /usr/bin/python3 update.py run.log 21意思是每天6点30分先切换到脚本目录再用python3执行update.py标准输出和错误输出都追加到run.log。注意python3要写完整路径可以用which python3查不要直接写python因为cron执行环境里的PATH往往和你终端里的不一样可能找不到命令。Windows下用任务计划程序打开任务计划程序创建基本任务触发器选「每天」时间设一个你不使用电脑的时段操作选「启动程序」程序填python.exe的完整路径比如C:\Python312\python.exe参数填update.py起始于填脚本所在目录。这里最容易翻车的是不填「起始于」导致脚本找不到同目录下的downloaded.txt和novel.txt。任务创建后可以右键手动运行一次确认能正常生成日志再放养。4.3 日志与失败重试脚本跑没跑跑成没跑成得有个交代定时任务运行在深夜里脚本失败了没人盯着。我吃过一次亏某天打开novel.txt发现更新停在两天前run.log里没有任何报错。原因是我最初没加日志模块所有print输出在没有终端时是看不见的。后来我统一改用logging写日志文件import logging logging.basicConfig( filenamerun.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, encodingutf-8, ) logging.info(开始更新目录解析到 %d 章, len(chapters))逻辑说明logging配置成把日志写入run.log文件编码用utf-8避免中文乱码。format里的%(asctime)s会自动记录时间这样第二天检查时能看到每轮任务的完整时间线。参数说明levellogging.INFO表示只记录INFO级别以上的日志不会把调试信息也写进去filename不写路径时日志文件就生成在脚本当前所在目录。除了日志还要给整个更新过程设置失败上限。我通常的做法是连续失败超过5章就停止本轮任务并把失败章节的URL写进failed.txt方便事后手动补。停止而不是无限重试是为了避免站点访问限制越来越严拖累后续所有章节。这个策略写进脚本后你基本可以每周看一次日志就够了。5. 下载器避坑记录这五个坑我都在真实场景里踩过这章直接进入我自己的踩坑记录每条都按现象、原因、解决三部分写。这些都是不需要你重复试错的经验。5.1 请求频率一高就被站点拒绝现象整本下载进行到几十章后后续请求返回的不是正文页面而是一个要求额外校验的中间页。我的parse_content解析结果变成空字符串脚本开始大量打印「正文为空」。原因连续请求间隔太短。我最初把延时设成0.5秒觉得这个频率足够慢但站点对同一边缘入口的短时间请求有自己的频率判断超过阈值就把后续请求引导到校验页。解决把每章之间的随机延时从0.5秒拉到1到3秒之间并用第3.3节的fetch_html_with_retry做指数退避。如果连续失败3次就停止整个脚本等10分钟后再跑。不要尝试开多线程加速下载小说站的数据量根本不需要并发单线程加延时是最不容易出问题的方案。5.2 正文里混进「上一章」「下一章」导航文字现象TXT里每章末尾都跟着一段「上一章」「下一章」的文字有的还带链接文本「返回目录」看起来像正文被污染了。原因部分站点把章节间的翻页导航放在正文容器内部我的第一个版本用全局正则提取正文把这些导航文字一股脑抓了进来。解决改成两层处理。第一解析时先定位正文容器只在容器内部提取文本第二用第3.2节的关键词过滤逐行清理。两个步骤配合使用而不是只靠关键词过滤因为导航文字的形式可能变容器定位相对稳定。5.3 章节标题重名导致漏章现象目录里某一章标题重复出现多次增量更新时只下载了第一次出现的那章后面几章全被当成已下载跳过了。原因记录文件里存的是章节标题重名标题在Set里只保留一个使命中去重失效。解决记录键改用章节URL。每章的URL在正常站点里都是唯一的不会因为标题重复而冲突。如果你的站点URL里带sortdesc这类临时参数就只截取不含参数的部分作为记录键避免同一章因为参数不同被重复下载。5.4 TXT打开乱码全是符号现象手机上看TXT正常Windows记事本打开全是乱码字符显示成黑菱形问号。原因写入文件用的UTF-8编码Windows记事本老版本默认按ANSI解码两者不一致导致乱码。解决写入时用utf-8-sig编码。它会在文件开头自动加上BOM标记记事本识别到BOM后用UTF-8解码乱码就消失了。代码改成open(output, w, encodingutf-8-sig)增量追加时同样用utf-8-sig。如果你更在意跨平台兼容而不是记事本体验也可以继续用纯UTF-8但我会优先选utf-8-sig因为多数场景下TXT文件都是在Windows上打开的。5.5 脚本半夜退出没人知道现象第二天检查novel.txt发现更新停留在前一天的某一章日志文件里没有任何报错。任务计划显示上次运行结果是成功但实际下载只跑了一半。原因脚本里print太多print输出在定时任务环境下不会有人看更关键的是我没给关键循环包异常处理某章正文解析遇到非预期HTML结构时抛了AttributeError整个进程直接退出。解决第一给主循环加try/except任何单章异常都记录到日志而不是中断整个进程第二设置一个可配置的失败上限连续失败超过上限就主动停止第三日志里必须写清楚「本次发现新章节X章、成功Y章、失败Z章」这样第二天扫一眼日志就能判断这次任务到底跑完没有。另外任务计划里如果设置了运行超时强制结束也建议关掉这个选项避免下载慢时被系统杀掉。6. 把更新器当服务跑一个检查清单和两个实战习惯脚本能跑、能更新、能定时执行之后建议花点时间做一个「自检清单」。每次修改脚本或发现异常时按这个顺序排查能省掉大量重复定位的时间检查项怎么查正常表现目录选择器是否失效打开浏览器访问站点比对新结构章节列表能解析出全部章节正文容器是否失效随机抽一章URL访问查看正文所在divparse_content返回非空字符串记录文件是否损坏查看downloaded.txt行数与已下载章节一致无重复行无空行日志是否有报错打开run.log看最近一次运行尾部显示新增章节数和失败章节数任务计划是否执行手动运行一次观察输出无新章节或正常追加两个实战习惯值得培养。第一个改完选择器后不要直接跑增量更新先清空记录文件把整本书重新下载到一个新文件比如novel_new.txt对比章节总数和文件大小正常后再切换回正式文件。这一步能防止你的改动触发误判把TXT搞乱。第二个保持「一周看一眼日志」的频率不要天天盯。如果连续三天失败数都大于0再去检查站点结构如果一周不跑也没失败说明脚本运行正常安心追更就好。我自己养成的最终习惯是所有临时实验都放在单独目录正式脚本和记录文件放在固定目录并且每次改完代码都手动跑一次全量到新文件验证。这个习惯救了我好几次。曾经有一次我为了加快速度把随机延时改成固定0.3秒结果下载到一半被站点拒绝整轮任务失败。后来我学乖了所有参数调整都要先在单章下载上试验确认没问题再放到定时任务里。希望这篇笔记里的思路和踩坑记录能帮到你让你少走几步弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑