在 Python 爬虫应用场景中,小说爬取是入门级且极具实用性的实战案例。《斗罗大陆》作为经典网络小说,章节数量多、内容结构清晰,非常适合用来讲解爬虫的核心逻辑与落地实现。本文将从需求分析、技术选型、代码实现到数据合并,完整拆解如何自动化抓取《斗罗大陆》全章节内容,并将分散的章节合并为完整的 TXT 文件,帮助你掌握爬虫开发的核心思路与实操技巧。
一、技术选型与核心思路
1. 核心技术栈
- 请求库:requests(发送 HTTP 请求,获取小说网页源码)
- 解析库:BeautifulSoup4(解析 HTML 源码,提取章节标题与内容)
- 文件操作:Python 内置文件处理模块(保存单章节内容,合并全本)
- 反爬应对:设置请求头(User-Agent),模拟浏览器请求
2. 核心实现思路
- 确定《斗罗大陆》小说的目标爬取站点(需选择结构清晰、无高强度反爬的站点);
- 爬取小说章节列表页,提取所有章节的标题与对应的 URL;
- 遍历章节 URL,逐个爬取章节内容,清理无关 HTML 标签,仅保留纯文本内容;
- 将每个章节的标题 + 内容保存为临时文件,或直接写入总文件;
- 合并所有章节内容,按章节顺序整理为完整的《斗罗大陆》TXT 文件。
二、完整代码实现
1. 环境准备
2. 完整爬虫代码
python
运行
import requests
from bs4 import BeautifulSoup
import time
import os
# 配置项
# 替换为实际的《斗罗大陆》章节列表页URL(需选择稳定的站点)
BASE_URL = "https://example.com/douluo/"
# 请求头,模拟浏览器访问
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",
"Referer": BASE_URL,
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9"
}
# ===== 新增代理配置 =====
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
# 构建代理字典(同时支持http和https)
proxies = {
"http": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
"https": f"https://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
}
# 保存路径
SAVE_DIR = "douluo_novel"
# 最终合并后的文件名
FINAL_FILE = "斗罗大陆-全本.txt"
def init_dir():
"""初始化保存目录"""
if not os.path.exists(SAVE_DIR):
os.makedirs(SAVE_DIR)
# 清空之前的临时文件(如果有)
if os.path.exists(os.path.join(SAVE_DIR, FINAL_FILE)):
os.remove(os.path.join(SAVE_DIR, FINAL_FILE))
def get_chapter_list():
"""爬取章节列表,返回[(章节标题, 章节URL), ...]"""
chapter_list = []
try:
# ===== 新增代理参数 proxies=proxies =====
response = requests.get(BASE_URL, headers=HEADERS, proxies=proxies, timeout=10)
response.encoding = response.apparent_encoding # 自动识别编码
soup = BeautifulSoup(response.text, "html.parser")
# ******** 关键解析逻辑(需根据目标站点调整)********
# 示例:假设章节列表在class为"chapter-list"的ul标签下,每个章节是li>a标签
chapter_items = soup.find("ul", class_="chapter-list").find_all("li")
for item in chapter_items:
a_tag = item.find("a")
if a_tag:
chapter_title = a_tag.get_text().strip() # 章节标题
chapter_url = a_tag.get("href") # 章节URL
# 拼接完整URL(如果是相对路径)
if not chapter_url.startswith("http"):
chapter_url = BASE_URL + chapter_url
chapter_list.append((chapter_title, chapter_url))
print(f"成功获取{len(chapter_list)}个章节")
return chapter_list
except Exception as e:
print(f"获取章节列表失败:{e}")
return []
def get_chapter_content(chapter_url):
"""爬取单个章节内容,返回纯文本内容"""
try:
# 增加随机延迟,避免请求过快触发反爬
time.sleep(0.5)
# ===== 新增代理参数 proxies=proxies =====
response = requests.get(chapter_url, headers=HEADERS, proxies=proxies, timeout=10)
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, "html.parser")
# ******** 关键解析逻辑(需根据目标站点调整)********
# 示例:章节内容在class为"content"的div标签下
content_tag = soup.find("div", class_="content")
if not content_tag:
return ""
# 清理无关标签(如广告、导航)
for unwanted in content_tag.find_all(["script", "style", "div", "p"], class_=["ad", "nav"]):
unwanted.extract()
# 提取纯文本并格式化(去除多余空格、换行)
content = content_tag.get_text().strip()
# 替换多个换行为单个换行,保证格式整洁
content = "\n".join([line.strip() for line in content.split("\n") if line.strip()])
return content
except Exception as e:
print(f"爬取章节失败 {chapter_url}:{e}")
return ""
def save_chapter(chapter_title, chapter_content, index):
"""保存单个章节到总文件"""
final_path = os.path.join(SAVE_DIR, FINAL_FILE)
# 追加写入,编码为utf-8避免中文乱码
with open(final_path, "a", encoding="utf-8") as f:
# 写入章节标题(带序号)
f.write(f"===== {index}. {chapter_title} =====\n\n")
# 写入章节内容
f.write(chapter_content)
# 章节间分隔
f.write("\n\n")
print(f"已保存:{index}. {chapter_title}")
def main():
"""主函数:爬取+合并全本"""
# 初始化目录
init_dir()
# 获取章节列表
chapter_list = get_chapter_list()
if not chapter_list:
print("未获取到章节列表,程序退出")
return
# 遍历章节,逐个爬取并保存
for index, (title, url) in enumerate(chapter_list, 1):
content = get_chapter_content(url)
if content:
save_chapter(title, content, index)
else:
print(f"跳过空章节:{title}")
print(f"\n爬取完成!全本已保存至:{os.path.join(SAVE_DIR, FINAL_FILE)}")
if __name__ == "__main__":
main()
三、代码关键解析与适配说明
1. 核心函数解析
- init_dir():初始化保存目录,确保文件存储路径存在,同时清理旧的全本文件,避免内容重复;
- get_chapter_list():核心是解析章节列表页,提取所有章节的标题和 URL。需重点注意:不同小说站点的 HTML 结构不同,你需要根据目标站点的源码调整
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">find()</font>/<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">find_all()</font>的参数(如 class 名称、标签类型); - get_chapter_content():爬取单章节内容,先通过 requests 获取页面源码,再用 BeautifulSoup 解析核心内容区,同时清理广告、脚本等无关标签,保证内容纯净;
- save_chapter():采用 “追加写入” 模式将章节内容写入总文件,避免多次打开关闭文件,提升效率,同时通过
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">utf-8</font>编码解决中文乱码问题; - main():串联整个流程,从初始化到章节爬取、保存,形成完整的执行链路。
2. 适配目标站点的关键调整
代码中标记<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">********</font>的部分是需要你根据实际爬取站点调整的核心解析逻辑,操作步骤如下:
- 打开目标小说站点的章节列表页,按 F12 打开开发者工具;
- 定位章节列表的 HTML 标签(如 ul/li/a),记录其 class/id 属性;
- 打开单个章节页,定位内容区的核心标签(如 div.content),替换代码中的解析参数;
- 测试爬取 1-2 个章节,验证内容提取是否准确,再批量爬取全本。
四、反爬应对与注意事项
- 请求频率控制:代码中加入
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">time.sleep(0.5)</font>,避免短时间内大量请求触发站点反爬机制,可根据站点宽松程度调整延迟时间(0.5-2 秒); - 请求头伪装:设置完整的 User-Agent、Referer 等参数,模拟真实浏览器请求,避免被识别为爬虫;
- 编码处理:使用
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">response.apparent_encoding</font>自动识别页面编码,解决中文乱码问题; - 合规性说明:本爬虫仅用于技术学习,爬取内容请勿用于商业用途,且需遵守目标站点的 robots 协议,避免违规爬取。
五、效果验证
运行代码后,会在当前目录下生成<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">douluo_novel</font>文件夹,其中的《斗罗大陆 - 全本.txt》即为合并后的完整小说文件。打开文件可看到:
plaintext
===== 1. 第一章 斗罗世界 =====
唐门外门弟子唐三,因偷学内门绝学为唐门所不容,跳崖明志时却发现没有死,反而以另外一个身份来到了另一个世界,一个属于武魂的世界,名叫斗罗大陆。
这里没有魔法,没有斗气,没有武术,却有神奇的武魂。每个人在六岁的时候,都会在武魂殿中令武魂觉醒。武魂有动物,有植物,有器物,武魂可以辅助人们的日常生活。而其中一些特别出色的武魂却可以用来修炼并进行战斗,这个职业,是斗罗大陆上最为强大也是最荣耀的职业——魂师。
===== 2. 第二章 武魂觉醒 =====
...
所有章节按顺序排列,内容无多余广告、格式整洁,实现了 “自动抓取 + 合并” 的核心目标。
总结
- 本次实战通过 requests+BeautifulSoup 实现了《斗罗大陆》小说的自动爬取,核心逻辑是 “解析章节列表→爬取单章节内容→追加写入合并文件”;
- 适配不同小说站点的关键是调整 HTML 解析参数,需结合开发者工具分析目标站点的标签结构;
- 爬虫开发需注意请求频率控制、编码处理和合规性,避免触发反爬或违规风险。