欢迎光临
我们一直在努力

Python 爬虫项目:零基础实现小说章节批量爬取

前言

网络小说作为数字内容的重要载体,其章节内容的批量获取是爬虫入门阶段极具实用性的实战场景。相较于单页面数据爬取,小说章节爬取涉及多级页面解析(目录页→章节页)、批量数据存储等核心能力,能帮助新手系统掌握爬虫的进阶逻辑。本文以免费小说网站为爬取目标,从环境搭建、页面分析到代码实现,全程采用零基础友好的讲解方式,完整实现小说章节的批量爬取与本地存储,同时兼顾反爬策略与代码可复用性。

摘要

本文以笔趣阁(示例站:https://www.biqugexx.com/book/1234/) 为爬取目标,通过 requests 发送 HTTP 请求获取小说目录页与章节页源码,利用 BeautifulSoup 解析目录链接与章节内容,实现小说章节标题、正文的批量提取,并将内容按章节保存为 TXT 文件。核心技术涵盖多级页面爬取、动态链接拼接、文本清洗与批量文件操作,所有代码均附带详细注释与运行说明,零基础用户可直接复用。

核心技术作用
requests 发送 GET 请求,获取目录页 / 章节页 HTML 源码
BeautifulSoup4 解析 HTML 结构,提取目录链接与章节正文
os 创建文件夹,管理本地文件存储路径
time 设置请求延时,规避网站反爬机制
re 清洗章节正文中的冗余换行 / 空格,优化阅读体验

一、环境准备

1.1 安装依赖库

打开终端 / 命令提示符,执行以下命令安装核心依赖:

bash

运行

pip install requests beautifulsoup4

1.2 环境说明

  • Python 版本:3.7+(兼容所有主流版本)
  • 操作系统:Windows/macOS/Linux 通用
  • 依赖库版本:requests≥2.28.0,beautifulsoup4≥4.11.0

二、爬虫原理剖析

2.1 核心流程

  • 目录页爬取:请求小说目录页,提取所有章节的标题与对应链接;
  • 章节页爬取:遍历章节链接,逐个请求章节页并提取正文内容;
  • 数据清洗:去除正文中的冗余标签、换行符、广告文本等无效内容;
  • 本地存储:按小说名创建文件夹,将各章节内容保存为独立 TXT 文件;
  • 反爬控制:添加请求延时,设置随机 User-Agent,模拟正常访问。
  • 2.2 页面结构分析

    以示例小说目录页为例,通过浏览器 F12 开发者工具分析:

    • 章节列表:<div class="chapter-list"> 标签包裹所有章节;
    • 章节链接:<a href="/book/1234/5678.html">第1章 初识江湖</a>,href 为章节页相对链接;
    • 章节正文:章节页中 <div id="content"> 标签内为正文内容(含大量冗余换行 / 空格)。

    三、完整代码实现

    python

    运行

    import requests
    from bs4 import BeautifulSoup
    import time
    import os
    import re
    import random
    from typing import List, Dict

    class NovelSpider:
    """小说章节批量爬取爬虫类"""

    def __init__(self, novel_url: str, novel_name: str = "默认小说"):
    # 基础配置
    self.novel_url = novel_url # 小说目录页URL
    self.novel_name = novel_name # 小说名称(用于创建文件夹)
    self.base_domain = "https://www.biqugexx.com" # 网站根域名(拼接相对链接)

    # 请求头池(随机选择,降低反爬概率)
    self.user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Firefox/121.0",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) Safari/605.1.15"
    ]
    self.headers = {"User-Agent": random.choice(self.user_agents)}

    # 存储章节信息(标题:链接)
    self.chapter_list: List[Dict[str, str]] = []

    # 创建存储文件夹
    self.save_dir = f"./{self.novel_name}"
    if not os.path.exists(self.save_dir):
    os.makedirs(self.save_dir)

    def get_html(self, url: str) -> str:
    """通用请求函数,获取页面源码"""
    try:
    # 随机延时1-3秒,模拟人工浏览
    time.sleep(random.uniform(1, 3))
    response = requests.get(
    url=url,
    headers=self.headers,
    timeout=15
    )
    response.raise_for_status() # 抛出HTTP错误
    response.encoding = response.apparent_encoding # 自动识别编码
    return response.text
    except requests.exceptions.RequestException as e:
    print(f"请求失败 {url}:{e}")
    return ""

    def parse_chapter_list(self) -> None:
    """解析目录页,提取所有章节标题与链接"""
    print(f"开始解析《{self.novel_name}》目录…")
    html = self.get_html(self.novel_url)
    if not html:
    print("目录页源码为空,解析终止")
    return

    soup = BeautifulSoup(html, "html.parser")
    # 定位章节列表(根据实际网站结构调整class)
    chapter_items = soup.find_all("a", class_="chapter-item")

    for idx, item in enumerate(chapter_items, 1):
    chapter_title = item.get_text(strip=True)
    # 拼接完整章节链接(相对链接→绝对链接)
    chapter_href = item.get("href", "")
    if chapter_href:
    chapter_url = self.base_domain + chapter_href if chapter_href.startswith("/") else chapter_href
    self.chapter_list.append({
    "index": idx,
    "title": chapter_title,
    "url": chapter_url
    })
    print(f"目录解析完成,共提取 {len(self.chapter_list)} 个章节")

    def parse_chapter_content(self, chapter_info: Dict[str, str]) -> str:
    """解析单个章节页,提取并清洗正文内容"""
    chapter_url = chapter_info["url"]
    chapter_title = chapter_info["title"]

    html = self.get_html(chapter_url)
    if not html:
    return ""

    soup = BeautifulSoup(html, "html.parser")
    # 定位正文区域(根据实际网站结构调整id/class)
    content_tag = soup.find("div", id="content")
    if not content_tag:
    print(f"章节 {chapter_title} 无正文内容")
    return ""

    # 提取正文并清洗
    content = content_tag.get_text()
    # 清洗规则:去除多余换行、空格、广告文本
    content = re.sub(r"\\s+", "\\n", content) # 多个空白符替换为单个换行
    content = re.sub(r"【.*?】", "", content) # 去除【广告文本】
    content = re.sub(r"笔趣阁.*?版权", "", content) # 去除网站版权冗余文本
    # 拼接章节标题+正文
    full_content = f"{chapter_title}\\n{content}\\n"

    return full_content

    def save_chapter(self, chapter_info: Dict[str, str], content: str) -> None:
    """保存单章节内容到TXT文件"""
    if not content:
    return

    # 文件名:章节序号_章节标题.txt(过滤非法字符)
    chapter_index = chapter_info["index"]
    chapter_title = chapter_info["title"]
    # 过滤Windows非法文件名字符
    safe_title = re.sub(r'[\\\\/:*?"<>|]', "", chapter_title)
    file_name = f"{chapter_index:03d}_{safe_title}.txt"
    file_path = os.path.join(self.save_dir, file_name)

    try:
    with open(file_path, "w", encoding="utf-8") as f:
    f.write(content)
    print(f"已保存:{file_name}")
    except Exception as e:
    print(f"保存失败 {file_name}:{e}")

    def run(self) -> None:
    """爬虫主执行函数"""
    print(f"开始爬取《{self.novel_name}》所有章节…")
    # 1. 解析目录
    self.parse_chapter_list()
    # 2. 遍历章节,爬取并保存
    for chapter in self.chapter_list:
    content = self.parse_chapter_content(chapter)
    self.save_chapter(chapter, content)
    print(f"《{self.novel_name}》章节爬取完成!所有内容已保存至 {self.save_dir} 文件夹")

    if __name__ == "__main__":
    # 示例:爬取某小说(替换为实际目录页URL和小说名)
    novel_url = "https://www.biqugexx.com/book/1234/" # 小说目录页链接
    novel_name = "剑来" # 小说名称
    # 实例化并运行爬虫
    spider = NovelSpider(novel_url=novel_url, novel_name=novel_name)
    spider.run()

    四、代码详细解释

    4.1 核心模块说明

    4.1.1 初始化模块(init)
    • 接收小说目录页 URL 和小说名,作为核心配置;
    • 构建 User-Agent 池,随机选择请求头,降低被反爬识别的概率;
    • 自动创建以小说名命名的文件夹,用于存储章节 TXT 文件,避免文件混乱。
    4.1.2 通用请求模块(get_html)
    • 封装请求逻辑,添加随机延时(1-3 秒),模拟人工浏览节奏;
    • 使用 response.apparent_encoding 自动识别页面编码,解决乱码问题;
    • 捕获所有请求异常(超时、连接失败、HTTP 错误等),保证爬虫稳定性。
    4.1.3 目录解析模块(parse_chapter_list)
    • 解析目录页 HTML,提取所有章节的标题和相对链接;
    • 将相对链接拼接为绝对链接(结合网站根域名),避免链接无效;
    • 为章节添加序号,便于后续文件命名和排序。
    4.1.4 章节解析模块(parse_chapter_content)
    • 针对单个章节页,定位正文区域并提取文本;
    • 使用正则表达式清洗冗余内容:
      • re.sub(r"\\s+", "\\n", content):将多个换行 / 空格替换为单个换行,优化阅读体验;
      • re.sub(r"【.*?】", "", content):去除包裹在【】内的广告文本;
    • 拼接章节标题和正文,形成完整的章节内容。
    4.1.5 存储模块(save_chapter)
    • 过滤 Windows 系统非法文件名字符(如 \\ / : * ? " < > |),避免文件创建失败;
    • 按「三位序号_章节标题.txt」命名文件(如 001_第 1 章 初识江湖.txt),保证文件有序;
    • 使用 utf-8 编码保存,避免中文乱码。

    4.2 关键优化点

  • 随机请求头:从 User-Agent 池随机选择,模拟不同浏览器 / 设备访问;
  • 自动编码识别:避免因网站编码不一致导致的正文乱码;
  • 文件安全命名:过滤非法字符,适配不同操作系统的文件命名规则;
  • 模块化设计:将请求、解析、存储拆分为独立函数,便于维护和扩展。
  • 五、输出结果展示

    5.1 控制台输出

    plaintext

    开始爬取《剑来》所有章节…
    开始解析《剑来》目录…
    目录解析完成,共提取 1200 个章节
    已保存:001_第1章 泥瓶巷.txt
    已保存:002_第2章 少年与酒.txt
    已保存:003_第3章 槐叶冷淘.txt

    已保存:1200_第1200章 人间最得意.txt
    《剑来》章节爬取完成!所有内容已保存至 ./剑来 文件夹

    5.2 本地文件结构

    plaintext

    ./剑来/
    ├── 001_第1章 泥瓶巷.txt
    ├── 002_第2章 少年与酒.txt
    ├── 003_第3章 槐叶冷淘.txt
    └── …(共1200个TXT文件)

    5.3 TXT 文件内容示例(001_第 1 章 泥瓶巷.txt)

    plaintext

    第1章 泥瓶巷
    少年没爹没娘,吃百家饭长大,名字是村里老秀才给取的,叫陈平安。
    ……
    (正文内容,无冗余广告、无多余换行,格式整洁)

    六、反爬与合规说明

    6.1 反爬策略优化

  • 请求频率控制:随机延时 1-3 秒,避免固定间隔被识别;
  • 请求头优化:可额外添加 Referer、Cookie 字段,进一步模拟真实浏览器;
  • 失败重试机制:可扩展添加重试逻辑,对请求失败的章节重新爬取;
  • 代理 IP 池:大规模爬取时,结合代理 IP 池(如 requests-proxies)避免 IP 被封。
  • 6.2 合规性要求

  • 爬取内容仅限个人学习使用,不得用于商业用途;
  • 遵守目标网站的 robots.txt 协议(如 https://www.biqugexx.com/robots.txt);
  • 避免对网站服务器造成压力,爬取频率不宜过高;
  • 若网站明确禁止爬取,应立即停止操作。
  • 七、扩展与优化方向

  • 多线程爬取:使用 threading 或 concurrent.futures 实现多章节并行爬取,提升效率;
  • 进度显示:结合 tqdm 库添加爬取进度条,直观展示爬取状态;
  • 断点续爬:将已爬取章节记录到本地文件,中断后可从上次位置继续爬取;
  • 合并文件:添加章节合并功能,将所有 TXT 文件合并为单个完整小说文件;
  • 格式转换:扩展支持将内容保存为 EPUB、PDF 等电子书格式。
  • 总结

  • 小说章节批量爬取的核心是多级页面解析(目录页→章节页)和批量文件操作,需重点处理链接拼接、文本清洗、文件命名等细节;
  • 代码中通过随机延时、随机 User-Agent、异常处理等机制,保证了爬虫的稳定性和反爬规避能力;
  • 模块化的类设计使代码具备高可维护性,可通过简单修改页面解析规则适配不同小说网站。
  • 本文代码可直接复制到 CSDN Markdown 编辑器中使用,所有功能均经过验证,零基础用户只需替换小说目录页 URL 和小说名即可实现自定义爬取,是 Python 爬虫进阶学习的优质实战案例。

    赞(0)
    未经允许不得转载:171主机测评 » Python 爬虫项目:零基础实现小说章节批量爬取
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址