1. 翻页章节链接 + 双页小说章节,到底难在哪
如果你正在用 Trae 配合 Python 写小说爬虫,大概率已经踩过两个坑:一是目录页本身要翻页,章节链接散落在index_1.html、index_2.html这种分页里;二是点进某一章后,正文只显示一半,底部写着"本章未完,点击下一页继续阅读",而第二页的地址规律是xxx_2.html。这两件事单独看都不复杂,叠在一起就容易乱:翻页时链接拼错、双页合并时顺序颠倒、断点续传把补充页当成新章节重复写入。
这篇要解决的就是这个组合场景。目标很明确:把"目录翻页抓链接"和"单章双页合并正文"做成一套可复用的配置化流程,你只需要改几个参数——网址、翻页页数、标题和正文的元素标签、保存路径——就能套到别的站点上。适合已经跑通过单页爬虫、想进一步处理复杂目录结构的人,也适合用 Trae 生成代码后不知道怎么调参的新手。
我试过把这两步拆开写,结果维护两套逻辑反而更乱,后来统一到一个process_single_chapter里,用is_supplement标记区分主页面和补充页,代码清爽很多。下面按"前置准备 → 配置骨架 → 验证 → 排障"的顺序展开,你可以直接复制去改。
2. 前置准备:TaoToken 与 Trae 的配合方式
在写爬虫之前,先把模型调用这条链路理顺。Trae 里做代码生成和调试时,如果直接连官方接口,网络波动和额度管理会比较麻烦,用 TaoToken 做统一入口会省心一些。它的官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 这条不带 UTM 参数。
具体操作上,你需要在 TaoToken 控制台创建一个 API Key,然后把它填到 Trae 的模型配置里。控制台入口在这里:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,创建 Key 的页面是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。拿到 Key 之后,如果你只是想先验证模型能不能正常对话,可以用模型对话页测试:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。
这里有个细节要注意:Trae 里配置自定义模型时,Base URL 填https://taotoken.net/api,不要带后面的路径,模型名按你实际用的填。配置完先发一句"你好"确认连通,再去写爬虫代码,避免把网络问题和代码问题混在一起排查。如果你打算长期用 Trae 做编码和 Agent 任务,可以了解下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
提示:API Key 不要硬编码进爬虫脚本里,也不要提交到 Git。建议用环境变量或单独的配置文件读取,Trae 生成代码时也提醒它别把 Key 写死。
3. 可复制的 Trae 任务配置与翻页参数骨架
这一节是核心。先给 Trae 一段清晰的 Prompt,让它按你的站点结构改代码。关键是把"翻页规律"和"双页规律"讲清楚,Trae 才能准确修改。
3.1 给 Trae 的 Prompt 模板
你可以直接把下面这段丢给 Trae,把方括号里的内容换成你的实际站点信息:
这是一个 Python + Selenium 的小说爬虫,需要处理两种翻页情况: 1. 目录页翻页:第1页是 [基础URL],第2页是 [基础URL]2/,第3页是 [基础URL]3/, 以此类推,最多翻到第 [N] 页。请把翻页范围做成可配置参数。 2. 单章双页:一个章节有两个页面,第1页链接形如 xxx.html, 第2页是在第1页链接后加 "_2" 再接 ".html",即 xxx_2.html。 要获取完整章节内容,需要先抓第1页正文,再抓第2页正文, 按顺序合并写入同一个章节标题下,第2页内容标注"(续)"。 请修改代码: - 翻页页数、基础URL、标题元素XPath、正文元素XPath、保存路径都做成变量,方便替换; - 双页合并时,第2页请求失败不能影响第1页已写入的内容; - 断点续传要能区分主页面和补充页,避免重复写入。Trae 拿到这段后,通常会帮你把get_parse_save_data里的翻页循环和process_single_chapter里的补充页逻辑对齐。下面是我实测下来比较稳的参数骨架。
3.2 翻页与双页的核心参数
把这几组参数单独拎出来,改站点时只动这里:
| 参数名 | 作用 | 示例值 |
|---|---|---|
base_url | 目录第1页地址 | https://example.com/indexlist/167845/ |
max_pages | 目录最多翻几页 | 10 |
page_pattern | 翻页URL拼接方式 | f"{base_url}{page}/" |
title_xpath | 章节标题选择器 | //*[@id="wrapper"]/article/h1 |
content_xpath | 正文选择器 | //*[@id="booktxt"]//p |
supplement_suffix | 补充页后缀规则 | _2 |
save_path | 保存目录 | D:\文档\大创\小说文本 |
翻页循环的骨架长这样,注意range(2, max_pages + 1)的边界:
# 处理第一页 all_chapter_links = self.get_chapter_links_from_page(base_url) # 处理后续页面,max_pages 可配置 for page in range(2, self.max_pages + 1): page_url = f"{base_url}{page}/" self.random_delay(3, 6) page_links = self.get_chapter_links_from_page(page_url) if page_links: all_chapter_links.extend(page_links) else: logging.warning(f"第{page}页无链接,停止翻页") break双页合并的关键在 URL 拼接。很多人写成xxx.html_2,正确做法是用os.path.splitext拆开扩展名再拼:
import os base, ext = os.path.splitext(chapter_url) # base=xxx, ext=.html chapter_url_2 = f"{base}_2{ext}" # 得到 xxx_2.html然后主页面和补充页分别调用同一个处理函数,用is_supplement区分:
# 主页面 self.process_single_chapter(chapter_url, index, total, f, is_supplement=False) # 补充页 self.process_single_chapter(chapter_url_2, index, total, f, is_supplement=True)在process_single_chapter内部,补充页的标题要加标识,避免和主页面混淆:
if is_supplement: chapter_title_text += "(续)"这样写入 TXT 后,同一章的两页内容会连在一起,阅读时不会断。
3.3 请求头与反爬参数
Selenium 这边主要靠ChromeOptions伪装。User-Agent 从列表里随机取,加上禁用自动化检测的选项:
options.add_argument(f'user-agent={random.choice(USER_AGENTS)}') options.add_argument('--disable-blink-features=AutomationControlled') options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_experimental_option('useAutomationExtension', False)随机延迟别省,random_delay(2, 5)在翻页和章节之间都调用一次,能明显降低被限流的概率。翻页间隔可以拉长到 3 到 6 秒,因为目录页请求更敏感。
4. 验证请求:用样例章节确认链接拼接与双页合并
配置改完别急着跑全本,先拿一个样例章节验证。这一步能帮你快速定位是翻页错了还是双页拼错了。
4.1 验证翻页链接拼接
先只跑目录抓取,把链接 dump 出来看:
python spider.py --url "https://example.com/indexlist/167845/" --dump-links --debug跑完检查生成的.links.txt文件,重点看三件事:第1页链接数量是否合理、第2页链接是否和第1页不重复、翻页到最后一页时有没有报错。如果第2页链接和第1页一样,说明page_pattern拼错了,检查base_url结尾有没有多余的斜杠。
4.2 验证双页合并结果
挑一个章节单独测。在代码里临时把all_chapter_links截断成前两个章节:
all_chapter_links = all_chapter_links[:2]跑完后打开 TXT,看第一个章节的内容。正常结果应该是:标题出现一次,正文连续,中间有"(续)"标识,没有重复段落。如果发现第2页内容跑到下一章去了,说明补充页的index传参有问题,检查process_single_chapter调用时index是否和主页面一致。
4.3 成功结果的判断标准
一次成功的抓取,日志里应该能看到这样的序列:处理第 1/2 章→处理第 1/2 章(补充部分)→处理第 2/2 章→处理第 2/2 章(补充部分)。如果补充部分频繁失败但主页面成功,多半是_2.html这个页面不存在,有些章节本身只有一页,这时候要允许补充页 404 时静默跳过,而不是报错中断。
# 补充页请求失败时,记录但不影响主流程 if not success and is_supplement: logging.info(f"章节 {index} 无补充页,跳过") return True5. 本篇常见错排查
5.1 翻页链接重复或漏抓
最常见的原因是base_url结尾斜杠处理不一致。有的站点第1页是.../167845/,第2页是.../167845/2/,如果你base_url写成.../167845,拼出来就变成.../1678452/。统一在配置里保证base_url以/结尾,翻页时直接f"{base_url}{page}/"。
另一个原因是翻页终止条件写错。用if not page_links: break比固定range更稳,因为不同站点总页数不一样,硬编码容易多翻出空页。
5.2 双页合并顺序颠倒
如果 TXT 里先出现"(续)"再出现正文,说明主页面和补充页的调用顺序反了。检查这两行的先后:
self.process_single_chapter(chapter_url, ...) # 必须先 self.process_single_chapter(chapter_url_2, ...) # 必须后还有一种情况是补充页 URL 拼成了xxx.html_2,请求直接 404,日志里会看到补充页全部失败。用os.path.splitext就能避免。
5.3 断点续传把补充页当新章节
downloaded_chapters集合里如果只存了主页面 URL,补充页每次都会重新抓。解决办法是把补充页 URL 也存进去,或者在判断时用主页面 URL 做 key:
# 用主页面URL作为进度key,补充页跟随主页面 progress_key = chapter_url.replace("_2.html", ".html") if progress_key in self.downloaded_chapters: return True5.4 正文选择器抓不到内容
不同站点正文容器差异很大,content_selectors列表里多放几个备选。如果所有选择器都失败,先手动打开页面用 F12 看正文的父元素 class 或 id,再补进列表。注意有些站点正文在 iframe 里,Selenium 需要先switch_to.frame才能抓到。
5.5 翻页时 driver 会话失效
长时间翻页容易遇到invalid session id。在翻页循环里加重建逻辑:
except Exception as e: if 'invalid session id' in str(e).lower(): self.init_driver() self.driver.get(self.url) time.sleep(2)配合max_retries重试,基本能扛住长任务。
6. 把流程固化下来,下次直接换参数
整套流程跑通后,你会发现真正需要改的只有开头那几行配置:base_url、max_pages、title_xpath、content_xpath、save_path,以及双页后缀规则_2。把这几个抽成配置文件或命令行参数,换站点时不用动核心逻辑。
如果你在 Trae 里做这类爬虫任务,建议把模型接入也固定下来,用 TaoToken 的 API 地址https://taotoken.net/api配合 API Key 管理,省得每次换环境重新配。长期做编码和 Agent 的话,Coding Plan 那条链路会更顺:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入细节看文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
最后留个实用习惯:每次改完配置,先跑--dump-links验证链接,再截断章节数验证双页合并,两步都过了再跑全本。这样出问题时能立刻定位是翻页层还是章节层,比一口气跑完再回头查日志省事得多。