Python爬虫实战2:小说爬虫(含源码)
别担心,今天我就带大家用Python亲手写一个简单实用的小说爬虫,实现从目录获取到章节内容抓取,再到最终保存为TXT文件的全流程。
🛠️ 准备工作:环境与库
在开始编码前,请确保你的Python环境已经安装了以下两个核心库。如果还没有,请在终端或命令行中运行以下命令进行安装:
pip install requests lxml
requests: 用于发送网络请求,获取网页的HTML源码。lxml: 一个高效的HTML/XML解析库,我们将用它来提取网页中的关键信息。
🏗️ 项目结构:让代码更清晰
为了让代码结构清晰、易于维护,我们将采用面向对象的方式,把配置和逻辑分离开来。整个爬虫主要由两个类构成:
NovelCrawlerConfig: 配置类,集中管理所有可变参数,如网址、请求头、保存路径等。NovelCrawler: 爬虫主类,包含获取目录、抓取内容、保存文件等核心逻辑。
💻 代码实战:分步解析
第一步:定义配置类 NovelCrawlerConfig
首先,我们创建一个配置类,把所有可能会用到的参数都放在这里。这样做的好处是,如果你想爬取另一本小说,只需要修改这里的几个变量即可,无需改动核心逻辑。
class NovelCrawlerConfig:
"""小说爬虫配置类"""
# 基础URL和目录页URL
BASE_URL = "https://m.shuke8.cc"
CATALOGUE_URL = "https://m.shuke8.cc/cygksjhsj/"
# 请求头,伪装成浏览器访问,防止被反爬
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/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
}
# 文件保存配置
OUTPUT_DIR = "Txt"
NOVEL_TITLE = "从野怪开始升级"
FILENAME = "从野怪开始升级.txt"
# 爬虫控制配置
REQUEST_TIMEOUT = 10 # 请求超时时间(秒)
REQUEST_DELAY = 1 # 每次请求间隔时间(秒),避免过于频繁
第二步:初始化爬虫类 NovelCrawler
接下来是爬虫的主类。在 __init__ 方法中,我们初始化配置、创建一个列表来存储章节链接,并检查输出目录是否存在。
import os
import time
from lxml import html
import requests
class NovelCrawler:
"""小说爬虫主类"""
def __init__(self, start_chapter_index=0):
self.config = NovelCrawlerConfig()
self.href_list = [] # 存储所有章节链接
self.start_chapter_index = start_chapter_index # 支持断点续爬
# 如果输出目录不存在,则创建
if not os.path.exists(self.config.OUTPUT_DIR):
os.mkdir(self.config.OUTPUT_DIR)
第三步:实现核心网络请求方法 fetch_page
这是一个基础但至关重要的方法,负责向目标URL发送请求并返回解析后的HTML对象。我们在这里加入了异常处理,确保网络波动不会导致程序直接崩溃。
def fetch_page(self, url):
"""获取网页内容"""
try:
# 禁用SSL警告,并发送GET请求
response = requests.get(url, headers=self.config.HEADERS, timeout=self.config.REQUEST_TIMEOUT, verify=False)
response.encoding = 'utf-8' # 明确指定编码
if response.status_code == 200:
return html.fromstring(response.text) # 使用lxml解析HTML
else:
print(f"请求失败,状态码: {response.status_code}")
return None
except Exception as e:
print(f"请求出错: {e}")
return None
第四步:获取章节目录 get_all_chapters
这个方法负责遍历小说的所有目录页,提取出每一章的标题和链接。通过观察目标网站,我们发现目录是分页的,所以需要一个循环来处理。
def get_chapter_list_from_page(self, url):
"""从单个目录页获取章节列表"""
document = self.fetch_page(url)
if not document: return []
chapter_list = []
# 使用XPath定位章节链接,这个路径需要根据实际网页结构调整
links = document.xpath('//*[@id="Body"]/div[4]/ol/li/a')
for link in links:
href = link.xpath('@href')[0]
text = link.xpath('text()')[0]
chapter_list.append({"href": href, "text": text})
print(f"发现章节: {text}")
return chapter_list
def get_all_chapters(self):
"""获取小说所有章节列表"""
print("开始获取章节列表...")
# 遍历所有目录页
for page_num in range(1, 34): # 假设最多33页目录
if page_num == 1:
url = f"{self.config.CATALOGUE_URL}1.html"
else:
url = f"{self.config.CATALOGUE_URL}1_{page_num}.html"
print(f"正在获取第 {page_num} 页目录...")
chapters = self.get_chapter_list_from_page(url)
self.href_list.extend(chapters)
time.sleep(self.config.REQUEST_DELAY) # 礼貌爬取,稍作停顿
print(f"总共获取到 {len(self.href_list)} 个章节")
第五步:抓取章节内容并保存 crawl_chapters
这是爬虫的最终目的。我们遍历上一步获取到的章节链接列表,逐个访问并提取正文内容,然后调用 save_chapter 方法写入文件。
def save_chapter(self, title, content):
"""保存章节内容到文件"""
filepath = os.path.join(self.config.OUTPUT_DIR, self.config.FILENAME)
with open(filepath, 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n{title}\n{'='*50}\n\n")
for paragraph in content:
if paragraph.strip():
f.write(f" {paragraph}\n\n")
def get_chapter_content(self, url, title):
"""获取单个章节的详细内容"""
print(f"正在获取: {title}")
document = self.fetch_page(url)
if not document: return False
try:
# 使用XPath提取正文段落
paragraphs = document.xpath('//*[@id="bodybox"]/p/text()')
if paragraphs:
content = [p.strip() for p in paragraphs if p.strip()]
self.save_chapter(title, content)
print(f"✓ {title} 获取成功!")
return True
else:
print(f"✗ {title} 未找到内容")
return False
except Exception as e:
print(f"✗ {title} 解析失败: {e}")
return False
def crawl_chapters(self):
"""爬取所有章节内容"""
if not self.href_list: return
print("\n开始爬取章节内容...")
success_count = 0
for index in range(self.start_chapter_index, len(self.href_list)):
chapter = self.href_list[index]
chapter_url = f"{self.config.BASE_URL}{chapter['href']}"
if self.get_chapter_content(chapter_url, chapter['title']):
success_count += 1
print(f"进度: {index + 1}/{len(self.href_list)} | 成功: {success_count}")
time.sleep(self.config.REQUEST_DELAY) # 再次停顿,做个“好公民”
print(f"爬取完成!总计成功: {success_count} 章")
第六步:主程序入口 main
最后,我们将所有步骤串联起来,形成一个完整的执行流程。
def run(self):
"""运行爬虫主流程"""
print(f"小说爬虫启动 - 《{self.config.NOVEL_TITLE}》")
self.get_all_chapters()
if self.href_list:
self.crawl_chapters()
else:
print("未获取到章节列表,程序退出")
def main():
# 可以设置从第几章开始爬取,0代表从头开始
crawler = NovelCrawler(start_chapter_index=0)
crawler.run()
if __name__ == '__main__':
main()
🚀 运行与测试
将以上所有代码整合到一个 .py 文件中(例如 novel_crawler.py)。在终端运行:
python novel_crawler.py
你会看到程序开始打印日志,显示正在获取目录、正在下载章节。稍等片刻,你就会在项目目录下发现一个名为 Txt 的文件夹,里面静静地躺着你的 从野怪开始升级.txt 了!
💡 总结与注意事项
通过这个实例,我们完成了一个功能完整的小说爬虫。它涵盖了网络请求、HTML解析、数据提取、文件IO和流程控制等爬虫开发的核心知识点。
最后,有几点非常重要的提醒:
- 遵守
robots.txt协议:在爬取任何网站前,请先查看其robots.txt文件,了解哪些内容允许爬取。
User-agent: *
Disallow: /member
Disallow: /ajax
- 控制请求频率:代码中使用了
time.sleep()来增加请求间隔,这是非常重要的“礼貌”行为,可以避免给目标服务器造成过大压力,也能有效降低IP被封禁的风险。 - 仅供学习交流:爬取的内容请仅用于个人学习和研究,切勿用于任何商业用途或大规模传播,尊重原作者和网站的版权。
- XPath路径非通用:代码中的XPath路径(如
'//*[@id="bodybox"]/p/text()')是针对示例网站特定的,爬取其他网站时需要你用浏览器的开发者工具(F12)重新分析并修改。
希望这篇教程能帮助你入门Python爬虫!如果有任何问题,欢迎在评论区留言讨论。
更多推荐

所有评论(0)