当前位置: 首页 > article >正文

Python:爬虫基础《爬取红楼梦》

小说爬虫项目说明文档

用于爬取诗词名句网上小说内容的Python爬虫项目。本项目以《红楼梦》为例,演示如何爬取完整的小说内容。

项目功能

  • 爬取小说的所有章节名称
  • 获取每个章节的URL链接
  • 下载并保存每个章节的内容到独立的文本文件
  • 自动创建存储目录
  • 包含基本的错误处理和请求延迟

环境要求

  • Python 3.x
  • 依赖包:
    • requests
    • beautifulsoup4
    • logging

安装依赖

pip install requests beautifulsoup4

项目结构说明

项目主要包含以下几个核心函数:

  1. extract_chapter_names(source): 提取所有章节名称
  2. extract_list_url(source, domain): 提取所有章节的URL
  3. extract_chapter_content(url_list, chapter_names, headers, folder_name): 下载并保存章节内容

实现步骤

1. 基础设置

import os
import requests
import logging
import time
from bs4 import BeautifulSoup

# 设置请求头
headers = {
    "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36"
}

2. 提取章节名称

extract_chapter_names 函数实现以下功能:

  • 查找包含章节列表的div元素
  • 提取所有章节的名称
  • 返回章节名称列表

extract_chapter_names 函数详细说明

函数概述

extract_chapter_names 函数用于从网页源代码中提取所有小说章节的名称。这个函数接收一个 BeautifulSoup 对象作为参数,返回一个包含所有章节名称的列表。

函数定义

def extract_chapter_names(source):
    page_list = source.find("div", class_="ContL")
    list_chapter = page_list.find("div", "list")
    a_list = list_chapter.findAll("a")
    chapter_names = []
    for a in a_list:
        print(a.get_text())
        chapter_names.append(a.get_text())
    return chapter_names

详细实现步骤

1. 定位主要内容区域

page_list = source.find("div", class_="ContL")
  • 使用 find() 方法查找类名为 “ContL” 的 div 元素
  • 这个 div 包含了小说的所有章节列表
  • class_="ContL" 是指定要查找的 CSS 类名

2. 定位章节列表区域

list_chapter = page_list.find("div", "list")
  • 在主要内容区域中查找类名为 “list” 的 div 元素
  • 这个 div 直接包含了所有章节的链接

3. 获取所有章节链接

a_list = list_chapter.findAll("a")
  • 使用 findAll() 方法获取所有的 <a> 标签
  • 每个 <a> 标签代表一个章节链接
  • 返回的是一个包含所有匹配元素的列表

4. 提取章节名称

chapter_names = []
for a in a_list:
    print(a.get_text())
    chapter_names.append(a.get_text())
  • 创建空列表 chapter_names 存储章节名称
  • 遍历每个 <a> 标签
  • 使用 get_text() 方法获取链接文本,即章节名称
  • 打印章节名称用于调试和进度显示
  • 将章节名称添加到列表中

5. 返回结果

return chapter_names
  • 返回包含所有章节名称的列表

示例输出

函数返回的列表格式如下:

[
    "第一回 甄士隐梦幻识通灵 贾雨村风尘怀闺秀",
    "第二回 贾夫人仙逝扬州城 冷子兴演说荣国府",
    # ... 更多章节名称
]

注意事项

  1. 函数依赖于网页的特定 HTML 结构,如果网站改版可能需要更新代码
  2. 确保传入的 source 参数是有效的 BeautifulSoup 对象
  3. 网页编码应该正确设置为 UTF-8,否则可能出现乱码
  4. 打印输出有助于监控爬取进度和调试

可能的改进

  1. 添加错误处理机制,处理元素不存在的情况
  2. 添加数据清洗功能,去除不必要的空白字符
  3. 可以添加章节编号提取功能
  4. 可以添加进度条显示替代简单的打印输出

3. 提取章节URL

extract_list_url 函数实现以下功能:

  • 查找所有章节的链接元素
  • 组合完整的URL地址
  • 返回URL列表

extract_list_url 函数详细说明

函数概述

extract_list_url 函数用于从网页源代码中提取所有章节的URL链接。这个函数接收两个参数:

  • source: BeautifulSoup 对象,包含解析后的网页内容
  • domain: 网站的域名,用于构建完整的URL

函数定义

def extract_list_url(source, domain):
    page_list = source.find("div", class_="ContL")
    list_chapter = page_list.find("div", "list")
    a_list = list_chapter.findAll("a")
    href_list = []
    for a in a_list:
        href = a.get("href")
        everyUrl = domain + href
        href_list.append(everyUrl)
    return href_list

详细实现步骤

1. 定位主要内容区域

page_list = source.find("div", class_="ContL")
  • 使用 find() 方法查找类名为 “ContL” 的 div 元素
  • 这个 div 是包含所有章节链接的容器
  • 与 extract_chapter_names 函数使用相同的定位方式

2. 定位章节列表区域

list_chapter = page_list.find("div", "list")
  • 在主要内容区域中查找类名为 “list” 的 div 元素
  • 这个 div 包含了所有章节的链接信息

3. 获取所有链接元素

a_list = list_chapter.findAll("a")
  • 使用 findAll() 方法获取所有的 <a> 标签
  • 每个 <a> 标签包含了章节的相对URL

4. 提取和组合URL

href_list = []
for a in a_list:
    href = a.get("href")
    everyUrl = domain + href
    href_list.append(everyUrl)
  • 创建空列表 href_list 存储完整URL
  • 遍历每个 <a> 标签
  • 使用 get("href") 获取链接的相对路径
  • 将域名和相对路径组合成完整的URL
  • 将完整URL添加到列表中

5. 返回结果

return href_list
  • 返回包含所有章节完整URL的列表

示例输出

函数返回的列表格式如下:

[
    "https://www.shicimingju.com/book/hongloumeng/1.html",
    "https://www.shicimingju.com/book/hongloumeng/2.html",
    # ... 更多章节URL
]

URL 结构说明

完整URL的构成:

  • 域名:https://www.shicimingju.com
  • 相对路径:/book/hongloumeng/章节序号.html
  • 组合后:https://www.shicimingju.com/book/hongloumeng/章节序号.html

注意事项

  1. 确保传入的 domain 参数末尾没有多余的斜杠,避免URL重复
  2. 函数依赖于网站的特定HTML结构,网站改版时需要更新代码
  3. 确保传入的 source 是有效的 BeautifulSoup 对象
  4. URL 组合时要注意路径的正确性

可能的改进

  1. 添加URL有效性验证
  2. 添加错误处理机制
  3. 可以添加URL格式检查
  4. 可以添加并行获取功能提高效率
  5. 添加URL去重机制
  6. 可以添加URL规范化处理
  7. 添加日志记录功能,方便调试和监控

4. 下载章节内容

extract_chapter_content 函数实现以下功能:

  • 创建保存小说的文件夹
  • 遍历所有章节URL
  • 下载并解析章节内容
  • 保存到独立的文本文件
  • 包含5秒延迟,避免请求过于频繁

extract_chapter_content 函数详细说明

函数概述

extract_chapter_content 函数是整个爬虫项目的核心函数,负责下载和保存小说内容。这个函数接收四个参数:

  • url_list: 所有章节的URL列表
  • chapter_names: 对应的章节名称列表
  • headers: HTTP请求头
  • folder_name: 保存小说内容的文件夹名称

函数定义

def extract_chapter_content(url_list, chapter_names, headers, folder_name):
    if not os.path.exists(folder_name):
        os.makedirs(folder_name)

    for i, url in enumerate(url_list):
        response = session.get(url, headers=headers)
        if response.status_code != 200:
            logging.error(f"请求失败,状态码: {response.status_code}")
        else:
            response.encoding = "utf-8"
            soup = BeautifulSoup(response.text, "html.parser")
            a = soup.find("div", class_="ContL")
            b = a.find("div", class_="contbox textinfor")
            c = b.find("div", class_="text p_pad")
            p_list = c.findAll("p")
            seen_texts = set()
            chapter_content = []
            for p in p_list:
                text = p.getText().replace('\xa0', ' ')
                if text not in seen_texts:
                    print(text)
                    chapter_content.append(text)
                    seen_texts.add(text)

            chapter_name = chapter_names[i]
            file_path = os.path.join(folder_name, f"{chapter_name}.txt")
            with open(file_path, "w+", encoding="utf-8") as file:
                file.write("\n".join(chapter_content))
            print(f"{chapter_name}章节的内容获取完毕")
        time.sleep(5)
        print(f"{i}章节的内容获取完毕")

详细实现步骤

1. 创建保存目录

if not os.path.exists(folder_name):
    os.makedirs(folder_name)
  • 检查保存目录是否存在
  • 如果目录不存在,则创建新目录
  • 使用 os.makedirs() 可以创建多层目录结构

2. 遍历章节URL

for i, url in enumerate(url_list):
  • 使用 enumerate() 同时获取索引和URL
  • 索引用于匹配章节名称
  • URL用于下载具体内容

3. 发送HTTP请求

response = session.get(url, headers=headers)
if response.status_code != 200:
    logging.error(f"请求失败,状态码: {response.status_code}")
  • 使用 session 发送GET请求
  • 检查响应状态码
  • 记录错误日志(如果请求失败)

4. 解析章节内容

response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "html.parser")
a = soup.find("div", class_="ContL")
b = a.find("div", class_="contbox textinfor")
c = b.find("div", class_="text p_pad")
p_list = c.findAll("p")
  • 设置正确的字符编码
  • 使用BeautifulSoup解析HTML
  • 逐层定位到具体内容区域
  • 获取所有段落内容

5. 内容去重和清理

seen_texts = set()
chapter_content = []
for p in p_list:
    text = p.getText().replace('\xa0', ' ')
    if text not in seen_texts:
        print(text)
        chapter_content.append(text)
        seen_texts.add(text)
  • 使用集合去除重复内容
  • 清理特殊字符(如\xa0)
  • 保存清理后的文本
  • 打印内容用于调试

6. 保存章节内容

chapter_name = chapter_names[i]
file_path = os.path.join(folder_name, f"{chapter_name}.txt")
with open(file_path, "w+", encoding="utf-8") as file:
    file.write("\n".join(chapter_content))
  • 获取对应的章节名称
  • 构建文件保存路径
  • 使用UTF-8编码保存文件
  • 用换行符连接各段落

7. 延迟处理

time.sleep(5)
print(f"{i}章节的内容获取完毕")
  • 添加5秒延迟避免请求过快
  • 打印进度信息

文件保存格式

每个章节保存为独立的文本文件:

  • 文件名:章节名.txt
  • 编码:UTF-8
  • 内容格式:段落间用换行符分隔

注意事项

  1. 确保网络连接稳定
  2. 注意请求频率限制
  3. 保持足够的磁盘空间
  4. 正确处理文件编码
  5. 注意错误处理和日志记录

可能的改进

  1. 添加断点续传功能
  2. 实现多线程下载
  3. 添加重试机制
  4. 增加进度条显示
  5. 支持自定义延迟时间
  6. 添加内容验证机制
  7. 支持不同的文本格式(如EPUB、PDF)
  8. 添加内容备份机制
  9. 实现错误自动恢复

使用方法

  1. 确保已安装所有依赖包
  2. 运行脚本:
python your_script.py

注意事项

  1. 请遵守网站的爬虫规则
  2. 代码中包含5秒的延迟,避免对目标网站造成压力
  3. 建议添加错误处理和日志记录
  4. 确保有足够的存储空间保存小说内容

输出结果

程序会在当前目录下创建一个"红楼梦"文件夹,其中包含所有章节的文本文件。每个文件以章节名命名,包含该章节的完整内容。

可能的改进

  1. 添加命令行参数支持不同小说的爬取
  2. 增加断点续传功能
  3. 添加更完善的错误处理
  4. 支持多线程下载
  5. 添加进度条显示
  6. 支持导出不同格式(如PDF、EPUB等)

完整代码示例


"""完成的代码如下所示"""
import os
import requests
import logging
import time
from bs4 import BeautifulSoup


def extract_chapter_names(source):
    page_list = source.find("div", class_="ContL")
    list_chapter = page_list.find("div", "list")
    a_list = list_chapter.findAll("a")
    chapter_names = []
    for a in a_list:
        print(a.get_text())
        chapter_names.append(a.get_text())
    return chapter_names  # 返回章节的名称列表


def extract_list_url(source, domain):
    # 抓取每一个章节的url
    page_list = source.find("div", class_="ContL")
    list_chapter = page_list.find("div", "list")
    a_list = list_chapter.findAll("a")
    href_list = []
    for a in a_list:
        href = a.get("href")
        everyUrl = domain + href
        href_list.append(everyUrl)
    return href_list  # 返回每一个章节对应的url


# 抓取单个小说的章节内容
def extract_chapter_content(url_list, chapter_names, headers, folder_name):
    if not os.path.exists(folder_name):
        os.makedirs(folder_name)

    for i, url in enumerate(url_list):
        response = session.get(url, headers=headers)
        if response.status_code != 200:
            logging.error(f"请求失败,状态码: {response.status_code}")
        else:
            response.encoding = "utf-8"
            soup = BeautifulSoup(response.text, "html.parser")
            a = soup.find("div", class_="ContL")
            b = a.find("div", class_="contbox textinfor")
            c = b.find("div", class_="text p_pad")
            p_list = c.findAll("p")
            seen_texts = set()
            chapter_content = []
            for p in p_list:
                text = p.getText().replace('\xa0', ' ')  # 处理NBSP
                if text not in seen_texts:
                    print(text)
                    chapter_content.append(text)
                    seen_texts.add(text)

            chapter_name = chapter_names[i]
            file_path = os.path.join(folder_name, f"{chapter_name}.txt")
            with open(file_path, "w+", encoding="utf-8") as file:
                file.write("\n".join(chapter_content))
            print(f"{chapter_name}章节的内容获取完毕")
        time.sleep(5)
        print(f"{i}章节的内容获取完毕")
    print("整个小说爬取完毕")


if __name__ == '__main__':
    url = "https://www.shicimingju.com/book/hongloumeng.html"
    headers = {
        "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/131.0.0.0 Mobile Safari/537.36 "
    }
    session = requests.session()
    page_source = session.get(url, headers=headers)

    # 检查请求是否成功
    if page_source.status_code != 200:
        logging.error(f"请求失败,状态码: {page_source.status_code}")
    else:
        page_source.encoding = "utf-8"
        domain = "https://www.shicimingju.com"
        soup = BeautifulSoup(page_source.text, "html.parser")
        chapter_names = extract_chapter_names(soup)
        list_url = extract_list_url(soup, domain)
        extract_chapter_content(list_url, chapter_names, headers, folder_name="./红楼梦") 

补充说明

可以使用多线程爬取或者携程爬取,当然多线程爬取就已经够用了,太快了不好。


http://www.kler.cn/a/466524.html

相关文章:

  • wordpress右侧浮动咨询台插件
  • 云架构Web端的工业MES系统设计之区分工业过程
  • 【2025年最新】OpenWrt 更换国内源的指南(图形界面版)
  • Coroutine 基础八 —— Flow 操作符(二)
  • 【每日学点鸿蒙知识】广告ID、NFC手机充值、CSS支持语法、PC与模拟器交互、SO热更新等
  • 操作系统大题整理
  • Redis Hash哈希
  • 使用Clion在ubuntu上进行交叉编译,并在Linux上远程编译五子棋
  • ABAQUS三维Voronoi晶体几何建模
  • 多台DHCP服务器时的问题讨论
  • Windmill 实战:快速构建自动化工作流和用户界面
  • Windows安装了pnpm后无法在Vscode中使用
  • AWS S3文件存储工具类
  • PyTorch AMP 混合精度中grad_scaler.py的scale函数解析
  • 使用函数求e的近似值(PTA)C语言
  • 【硬件测试】基于FPGA的2FSK+帧同步系统开发与硬件片内测试,包含高斯信道,误码统计,可设置SNR
  • 阻抗(Impedance)、容抗(Capacitive Reactance)、感抗(Inductive Reactance)
  • 【Docker】:Docker容器使用
  • 野火直播 5.9.5 | 频道丰富的电视直播软件,支持港澳台频道和快速换台
  • 【深度学习】Java DL4J基于 LSTM 构建新能源预测模型
  • vue学习第一阶段
  • MacBook_Xcode_Swift雨燕
  • 深度学习中的离群值
  • 27、使用StreamPark管理Flink作业中,关于对Flink客户端的conf进行占位符替换的修改
  • 将数组转换为laravel中的对象
  • ts中 type 和 interface 定义类型有什么区别?