批量爬取小说章节并优化排版(附完整可运行脚本)
在日常学习和生活中,我们经常会遇到想要保存喜欢的网络小说以便离线阅读的需求。手动复制粘贴每一章内容不仅耗时费力,还容易出现排版混乱、格式错乱等问题。今天就带大家实战一款Python小说爬虫脚本,实现批量爬取指定小说章节、自动优化排版(还原小说段落换行、空行格式)、自动创建文件夹保存章节文件,全程代码可直接复制运行,新手也能快速上手。
本文将从页面标签结构分析、核心知识点拆解、完整脚本实现、脚本运行说明四个方面展开,手把手教你搭建属于自己的小说爬虫,最后预告下一篇内容------多线程优化脚本,解决单线程爬取速度慢的问题,进一步提升爬取效率。
一、实战前提与环境准备
在开始实战之前,我们需要准备好基础的Python环境和相关依赖库,确保脚本能够正常运行。
1.1 环境要求
-
Python版本:3.7及以上(推荐3.9,兼容性更好)
-
依赖库:requests(发送HTTP请求)、lxml(解析HTML页面)
1.2 依赖库安装
打开CMD终端,输入以下命令安装所需依赖库,安装速度慢的话可以切换国内镜像源(如阿里云、豆瓣源):
bash
pip install requests lxml -i https://pypi.doubanio.com/simple/
安装完成后,可通过以下命令验证是否安装成功:
bash
pip list | findstr "requests lxml" # Windows系统
pip list | grep "requests lxml" # Linux/Mac系统
若能看到requests和lxml的版本信息,说明依赖库安装成功。
二、目标页面标签结构分析(核心关键)
爬虫的核心是"找到目标内容",而找到目标内容的前提是分析页面的HTML标签结构。本次实战的目标是爬取小说的章节列表和每一章的正文内容,我们先通过浏览器的"开发者工具"分析页面结构,这也是爬虫实战中最基础、最重要的一步。
2.1 章节列表页面结构分析
我们首先访问小说的章节列表页面(本文隐藏具体链接,实际使用时替换为目标小说章节列表页即可),右键点击页面空白处,选择"检查"(或按F12快捷键)打开开发者工具,切换到"Elements"标签,即可查看页面的HTML结构。
通过分析发现,所有章节的链接都存放在一个无序列表
- 中,每个章节对应一个列表项
- ,列表项内部包含一个标签,标签的href属性就是章节详情页的链接(相对路径)。
关键标签结构简化如下(保留核心部分):
html
<body>
<div class="main">
<div class="chapter-list">
<ul>
<li><a href="/book/xxxx/1.html">第1章 开篇</a></li>
<li><a href="/book/xxxx/2.html">第2章 相遇</a></li>
<li><a href="/book/xxxx/3.html">第3章 转折</a></li>
<!-- 更多章节... -->
</ul>
</div>
</div>
</body>
基于这个结构,我们可以使用XPath表达式定位到所有的
- 标签,进而提取每个章节的链接。本次实战中,我们使用的XPath表达式为:
//html/body/div[1]/div[4]/ul/li,该表达式可以精准定位到章节列表的所有列表项。
2.2 章节详情页结构分析
点击任意一个章节链接,进入章节详情页,同样通过开发者工具分析正文和标题的标签结构。
-
章节标题:章节标题存放在id为"neirong"的
标签内部的
标签中,XPath表达式可定位为:
//*[@id="neirong"]/h1/text(),通过text()方法提取标题文本。 -
章节正文:章节正文存放在id为"txt"的
标签中,该标签内部包含大量的
标签(用于换行)和文本内容。由于
标签会导致文本换行,直接提取文本会丢失换行格式,因此我们需要提取该标签下的所有文本节点,再重新排版。
章节详情页核心标签结构简化如下:
html
<div id="neirong">
<h1>第1章 开篇</h1>
<div id="txt">
<p>清晨的阳光透过窗户,洒在书桌的旧书上。<br/>
主角揉了揉眼睛,缓缓坐起身,脑海中还残留着昨晚的梦境。<br/>
窗外的鸟鸣声此起彼伏,预示着新的一天开始了。<br/>
<!-- 更多正文内容... -->
</p>
</div>
</div>
这里需要注意的是,
标签是HTML中的换行标签,在爬取时,我们需要将这些换行转换为文本中的换行符(\n),同时清理多余的空格和空行,让排版符合小说阅读习惯(段落之间空一行,标题上下空行)。
三、核心知识点拆解(新手必看)
本次实战脚本虽然简短,但包含了Python爬虫的核心知识点,掌握这些知识点,能够帮助你应对大部分基础的静态页面爬虫需求。
3.1 requests库:发送HTTP请求
requests库是Python中最常用的HTTP请求库,简洁易用,能够快速发送GET、POST等请求,获取网页内容。本次实战中,我们使用requests.get()方法发送GET请求,获取章节列表页和章节详情页的HTML内容。
关键知识点:
-
headers参数:模拟浏览器发送请求,避免被网站反爬。其中User-Agent是最常用的参数,用于告诉网站服务器"我是一个正常的浏览器访问,不是爬虫"。
-
response.encoding:设置网页编码格式,避免出现乱码。本次实战中我们设置为"utf-8",适配大多数中文网站。
-
response.text:获取网页的HTML文本内容,用于后续解析。
3.2 lxml库与XPath:解析HTML页面
lxml库是一款高效的HTML/XML解析库,支持XPath表达式,能够快速定位和提取页面中的目标内容。XPath是一种在XML文档中查找信息的语言,也适用于HTML文档,语法简洁、定位精准,是爬虫解析页面的首选工具。
本次实战中常用的XPath语法:
-
//*[@id="txt"]:定位id为"txt"的任意标签(*表示任意标签)。 -
//*[@id="neirong"]/h1/text():定位id为"neirong"的标签下的h1标签,并提取其文本内容。 -
//*[@id="txt"]//text():定位id为"txt"的标签下的所有文本节点(包括子标签中的文本),//表示递归查找所有子节点。 -
./a/@href:定位当前节点下的a标签,并提取其href属性值(相对路径链接)。
3.3 os模块:文件与文件夹操作
os模块是Python中用于操作文件和文件夹的内置模块,本次实战中我们使用os.path.exists()方法判断文件夹是否存在,使用os.mkdir()方法创建文件夹,确保章节文件能够正常保存到指定目录。
关键代码解析:
python
if not os.path.exists("novel"):
os.mkdir("novel")
这段代码的作用是:判断当前目录下是否存在"novel"文件夹,如果不存在,则创建该文件夹,避免后续写入文件时出现"路径不存在"的报错。
3.4 文本排版优化
网络小说的正文通常包含大量的
换行标签,直接提取文本会导致所有内容连在一起,阅读体验极差。本次实战中,我们通过以下步骤优化排版:
-
提取id为"txt"的标签下所有文本节点,存入列表。
-
遍历列表,清理每个文本节点的多余空格(strip()方法),过滤掉空文本。
-
用"\n\n"拼接所有文本,实现段落之间空一行的效果(小说标准排版)。
-
给章节标题上下各添加空行,让标题与正文区分明显,提升阅读体验。
四、完整可运行脚本(实战核心)
以下是本次实战的完整脚本,已修复所有报错(如chapter_title定义顺序错误),优化排版逻辑,隐藏具体书号和链接,大家只需替换url_part和url_html为目标小说的域名和章节列表页链接,即可直接运行,实现批量爬取10章小说内容。
python
import requests
from lxml import etree
import os
# 确保novel文件夹存在,不存在则创建(用于保存章节文件)
if not os.path.exists("novel"):
os.mkdir("novel")
# 小说网站域名前缀(替换为目标小说网站的域名)
url_part = "https://www.biquge365.net"
# 小说章节列表页链接(替换为目标小说的章节列表页链接)
url_html = "https://www.biquge365.net/newbook/xxxx/"
# 请求头:模拟浏览器访问,避免被反爬
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
}
# 发送请求,获取章节列表页HTML内容
response = requests.get(url=url_html, headers=headers)
# 设置编码格式,避免乱码
response.encoding = "utf-8"
# 解析HTML页面
tree = etree.HTML(response.text)
# 定位所有章节的列表项(li标签),提取章节链接
li = tree.xpath("/html/body/div[1]/div[4]/ul/li")
# 章节计数:控制爬取的章节数量
chapter_count = 0
# 最大爬取章节数(可根据需求修改)
max_chapter = 10
# 遍历所有章节列表项,爬取每一章内容
for i in li:
# 爬够指定章节数后停止
if chapter_count >= max_chapter:
break
# 提取章节详情页的相对路径链接
a = i.xpath("./a/@href")[0]
# 拼接完整的章节详情页链接(绝对路径)
url = url_part + a
# 发送请求,获取章节详情页内容
response = requests.get(url=url, headers=headers)
response.encoding = "utf-8"
# 解析章节详情页HTML
tree = etree.HTML(response.text)
# 提取章节标题(id为neirong的div下的h1标签)
title = tree.xpath('//*[@id="neirong"]/h1/text()')
# 定义章节标题,避免未定义报错(如果获取不到标题,自动命名为"第X章")
chapter_title = title[0].strip() if title else f"第{chapter_count + 1}章"
# 提取章节正文(id为txt的div下的所有文本节点)
text_list = tree.xpath('//*[@id="txt"]//text()')
# 优化排版:清理空格、空文本,按小说格式换行(段落之间空一行)
lines = []
for t in text_list:
line = t.strip()
# 过滤掉空文本,避免多余空行
if line:
lines.append(line)
# 拼接正文内容,实现段落之间空一行
content = "\n\n".join(lines)
# 拼接完整章节内容:标题上下空行,提升阅读体验
full_content = f"\n{chapter_title}\n\n{content}\n\n"
# 写入文件:保存到novel文件夹,每个章节一个单独的txt文件
with open(f"novel/{chapter_title}.txt", "w", encoding="utf-8") as f:
f.write(full_content)
# 打印爬取进度
print(f"已成功写入:{chapter_title}")
# 章节计数加1
chapter_count += 1
# 爬取完成提示
print(f"\n爬取完成!共爬取{max_chapter}章,文件已保存至当前目录下的novel文件夹中。")
五、脚本运行说明与注意事项
5.1 运行步骤
-
复制上述完整脚本,粘贴到Python编辑器(如PyCharm、VS Code、IDLE等)中。
-
替换脚本中的url_part和url_html:
-
url_part:目标小说网站的域名(如https://www.xxx.com)。
-
url_html:目标小说的章节列表页链接(打开小说章节列表,复制浏览器地址栏中的链接即可)。
-
修改max_chapter(可选):如果想爬取更多章节,可修改max_chapter的值(如改为20,即爬取20章)。
-
运行脚本,等待爬取完成,脚本会自动创建novel文件夹,并将每一章内容保存为单独的txt文件。
5.2 注意事项
-
反爬处理:脚本中已添加User-Agent请求头,模拟浏览器访问,避免被网站反爬。如果爬取时出现403、503等报错,可更换User-Agent(百度搜索"User-Agent大全",复制任意一个浏览器的User-Agent替换即可)。
-
编码问题:如果爬取的内容出现乱码,可尝试将response.encoding改为"gbk"(部分中文网站使用gbk编码)。
-
标签结构变化:如果脚本运行时出现"列表索引超出范围"的报错,大概率是目标网站的标签结构发生了变化,需要重新通过开发者工具分析页面结构,修改对应的XPath表达式。
-
合规爬取:本脚本仅用于学习和个人研究,请勿用于商业用途,爬取时请遵守网站的robots协议,不要过度频繁爬取,避免给网站服务器造成压力。
六、下一篇预告:多线程优化脚本
本次实战的脚本采用单线程爬取方式,虽然逻辑简单、易于理解,但存在一个明显的问题:爬取速度慢。因为单线程需要等待一个章节爬取完成后,才能开始爬取下一个章节,尤其是当需要爬取大量章节(如几百章、几千章)时,耗时非常长。
下一篇文章,我们将针对这个问题,使用Python的threading模块(多线程)或concurrent.futures模块(线程池)对本次的爬虫脚本进行优化,实现多章节同时爬取,大幅提升爬取效率。
下一篇核心内容预告:
-
多线程原理讲解:为什么多线程能提升爬取速度?
-
线程池的使用:避免手动创建过多线程,提升脚本稳定性。
-
多线程脚本修改:在本次脚本基础上,添加多线程逻辑,实现批量快速爬取。
-
多线程爬取注意事项:避免并发过高被反爬,控制线程数量。
总结
本次Python爬虫实战,我们实现了从页面标签分析、核心知识点拆解到完整脚本编写的全过程,成功实现了批量爬取小说章节、自动优化排版、自动保存文件的功能。通过本次实战,相信大家已经掌握了基础的静态页面爬虫技巧,包括requests发送请求、lxml解析页面、XPath定位内容、os模块操作文件等核心知识点。
脚本已优化至无报错、排版美观,新手可直接复制运行,只需替换对应的链接即可。下一篇我们将通过多线程优化脚本,解决单线程爬取速度慢的问题,让爬虫效率再上一个台阶,感兴趣的小伙伴可以持续关注!
如果在运行脚本过程中遇到问题,欢迎在评论区留言,我会一一解答。记得点赞、收藏、关注,后续会分享更多Python爬虫实战教程!