前言
网络爬虫是一个非常有趣且实用的技术,尤其是在我们需要从网站上自动获取数据时。在这篇博客中,我将分享如何使用 Python 的 requests
和 lxml
库来编写一个简单的爬虫,从某个小说网站上爬取整本小说的章节内容,并将其保存为一个本地的文本文件。
我们将介绍逐行的代码解析、爬取流程,以及如何处理特殊情况,比如爬取到最后一章时终止程序。
使用的工具和库
在这次项目中,我们主要用到以下几个 Python 库:
requests
:用于发送 HTTP 请求并获取网页的 HTML 内容。lxml
:用于解析 HTML 内容,帮助我们使用 XPath 定位并提取需要的数据。time
和random
:用于添加延迟和生成随机的等待时间,防止频繁请求被服务器封禁。
在开始之前,确保你已安装了这些库,可以通过以下命令安装:
pip install requests
pip install lxml
爬虫流程
这个爬虫的任务是逐章获取小说内容,并将每一章的内容保存到本地文件中。以下是主要的爬取步骤:
- 获取小说初始章节的 URL。
- 发送 HTTP 请求,获取页面内容。
- 解析 HTML 页面,提取章节标题和正文内容。
- 将内容保存到本地文件。
- 查找下一章的链接,并重复上述步骤。
- 当爬取到最后一章时,程序终止。
完整代码
import requests
import time
import random
from lxml import etree
# 初始页面URL和最终页面URL
url = 'https://dldl1.nsbuket.cc/xiaoshuo/douluodalu/1.html'
final_url = 'https://dldl1.nsbuket.cc/xiaoshuo/douluodalu/'
# 创建会话,便于维持长连接
session = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0',
}
# 爬取小说
while url:
try:
# 添加随机延迟,防止过快爬取
time.sleep(random.uniform(3, 5))
# 发送GET请求获取页面内容
resp = session.get(url, headers=headers)
resp.encoding = 'utf-8' # 确保处理中文字符
# 解析页面HTML
e = etree.HTML(resp.text)
# 获取章节标题和正文
info = '\n'.join(e.xpath('//div[@class="m-post"]/p/text()'))
title = e.xpath('//h1/text()')[0]
# 将章节内容写入文件
with open('斗罗大陆.txt', 'a', encoding='utf-8') as f:
f.write(title + '\n\n' + info + '\n\n')
print(f"已保存: {title}")
# 获取下一章链接
next_link = e.xpath("//tr/td[2]/a/@href")
if next_link:
next_url = f'https://dldl1.nsbuket.cc{next_link[0]}'
if next_url == final_url:
print("已到达最后一章,爬取结束。")
break
else:
url = next_url
else:
print("没有找到下一章的链接,爬取结束。")
break
except requests.exceptions.RequestException as e:
print(f"请求出现错误: {e}")
time.sleep(5) # 出现错误时等待5秒再重试
except Exception as ex:
print(f"发生错误: {ex}")
break
定义初始和结束 URL
url = 'https://dldl1.nsbuket.cc/xiaoshuo/douluodalu/1.html'
final_url = 'https://dldl1.nsbuket.cc/xiaoshuo/douluodalu/'
url
: 小说第一章的 URL,是程序爬取的起点。final_url
: 定义最后一章的 URL,当爬虫遇到这个 URL 时会停止。
创建会话和请求头
session = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0',
}
session
:requests.Session()
用于创建会话,这样可以在多个请求之间共享 cookies 和其他参数,提高效率。headers
: 设置User-Agent
模拟浏览器访问,避免网站因为识别到爬虫而拒绝请求。
爬取循环
while url:
try:
time.sleep(random.uniform(3, 5)) # 添加随机延迟,防止被封
resp = session.get(url, headers=headers)
resp.encoding = 'utf-8'
e = etree.HTML(resp.text)
- 循环结构:
while url
只要有下一章的 URL,程序就会继续循环爬取。 - 延迟请求:
time.sleep(random.uniform(3, 5))
生成 3 到 5 秒之间的随机延迟,模拟正常的访问行为。 - 发送请求:
resp = session.get(url, headers=headers)
向服务器发送请求,获取章节页面。 - 设置编码:
resp.encoding = 'utf-8'
确保可以正确处理中文字符。 - 解析 HTML:
etree.HTML(resp.text)
将获取到的 HTML 转换成可供 XPath 操作的树状结构。
提取内容和保存
info = '\n'.join(e.xpath('//div[@class="m-post"]/p/text()'))
title = e.xpath('//h1/text()')[0]
with open('斗罗大陆.txt', 'a', encoding='utf-8') as f:
f.write(title + '\n\n' + info + '\n\n')
print(f"已保存: {title}")
- 提取正文:
e.xpath('//div[@class="m-post"]/p/text()')
用 XPath 提取小说正文,每一段文本用换行符'\n'.join()
拼接在一起。 - 提取标题:
e.xpath('//h1/text()')[0]
提取章节标题。 - 保存内容: 打开或创建一个名为
斗罗大陆.txt
的文件,并将章节标题和正文内容按顺序追加写入文件中。 - 打印信息:
print(f"已保存: {title}")
输出保存的章节标题,帮助我们实时监控爬取进度。
查找下一章
next_link = e.xpath("//tr/td[2]/a/@href")
if next_link:
next_url = f'https://dldl1.nsbuket.cc{next_link[0]}'
if next_url == final_url:
print("已到达最后一章,爬取结束。")
break
else:
url = next_url
else:
print("没有找到下一章的链接,爬取结束。")
break
- 查找下一章链接:
e.xpath("//tr/td[2]/a/@href")
使用 XPath 找到下一章的链接。如果有链接,程序会将其与主域名拼接成完整的 URL。 - 结束条件: 如果下一章的 URL 是我们设定的
final_url
,说明已经爬取到最后一章,程序终止爬取。 - 没有找到下一章: 如果没有找到下一章的链接,爬虫会输出信息并终止爬取。
异常处理
except requests.exceptions.RequestException as e:
print(f"请求出现错误: {e}")
time.sleep(5)
except Exception as ex:
print(f"发生错误: {ex}")
break
- 异常捕获: 如果在请求过程中发生网络错误,程序不会崩溃,而是等待 5 秒后重试。其他异常将终止爬取并输出错误信息。
小结
通过这篇博客,我们构建了一个简单但功能强大的爬虫。它能够自动抓取小说的多章节内容,并将它们按顺序保存到本地文件。我们实现了以下功能:
- 随机延迟防止爬虫被封禁。
- 处理章节内容和链接的提取。
- 保存每章内容到文件。
- 到达最后一章时结束爬取。
这只是爬虫的一个基础示例,针对不同网站的结构,爬虫可以进一步扩展。希望这篇文章能够为你提供一些启发,让你在爬虫的世界里更进一步!