目录
适用场景:需要整本连续阅读、进行文本分析或训练NLP模型的情况
适用场景:需要分章节处理、建立小说数据库或进行章节级分析的情况
爬取在线小说资源是Python网络爬虫的经典应用场景。本文将通过两个优化版本的代码,演示如何实现整本小说抓取的两种不同存储方案。两种方案均已加入防封禁策略和内容清洗功能,读者可根据需求选择使用。(仅供技术研究,请遵守网站规则)
方案一:单文件完整版(一体化存储)
核心特点:
-
全本内容存储为单个txt文件
-
追加写入模式节省内存
-
章节间自动添加分隔符
from bs4 import BeautifulSoup
from curl_cffi import requests as cffi_requests
import os
import time
import random
# 配置参数
BASE_URL = "https://www.beqege.cc/2/2{}.html" # 统一URL模板
SAVE_PATH = os.path.join("D:", "小说") # 存储路径
FILE_NAME = "凡人修仙传(完整版).txt" # 统一文件名
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.beqege.cc/"
}
# 创建存储目录(确保只执行一次)
os.makedirs(SAVE_PATH, exist_ok=True)
# 完整文件路径
full_path = os.path.join(SAVE_PATH, FILE_NAME)
for chapter_num in range(1, 2456):
# 添加随机延迟防止封禁
time.sleep(random.uniform(0.5, 2))
try:
# 请求页面
response = cffi_requests.get(
BASE_URL.format(chapter_num),
impersonate='chrome110',
headers=HEADERS,
timeout=15,
verify=False # 保持与原始设置一致
)
response.raise_for_status()
# 解析内容
soup = BeautifulSoup(response.text, "lxml")
# 提取章节标题(更精准的选择器)
chapter_title = soup.find("div", class_="bookname").h1.text.strip()
# 提取正文内容(优化选择器)
content_div = soup.find("div", id="content")
# 清理广告等无关内容
[x.decompose() for x in content_div.find_all("div", class_="ads")]
chapter_content = "\n".join([p.text.strip() for p in content_div.find_all("p")])
# 写入文件(追加模式)
with open(full_path, "a", encoding="utf-8") as f:
f.write(f"\n\n{chapter_title}\n\n") # 添加章节分隔符
f.write(chapter_content)
f.write("\n\n\n") # 章节结束
print(f"已保存第 {chapter_num} 章:{chapter_title}")
except Exception as e:
print(f"第 {chapter_num} 章抓取失败,错误信息:{str(e)}")
# 可以在此添加重试逻辑
print("全部章节抓取完成!")
技术亮点:
-
流量模拟技术:
-
使用
curl_cffi
库模拟Chrome110浏览器TLS指纹 -
随机延时(0.5-2秒)配合动态UA降低被封风险
-
-
精准内容提取:
# 定位章节标题 chapter_title = soup.find("div", class_="bookname").h1.text.strip() # 提取正文并清理广告 content_div = soup.find("div", id="content") [x.decompose() for x in content_div.find_all("div", class_="ads")]
-
健壮性增强:
-
异常捕获模块记录失败章节
-
自动创建存储目录(
exist_ok=True
防重复创建)
-
适用场景:需要整本连续阅读、进行文本分析或训练NLP模型的情况
方案二:分章节存储版(模块化管理)
核心特点:
-
按章节独立存储为txt文件
-
自动生成规范化文件名
-
原子写入防止数据损坏
from bs4 import BeautifulSoup
from curl_cffi import requests as cffi_requests
import os
import time
import random
import re
# 配置参数
BASE_URL = "https://www.beqege.cc/2/2{}.html"
SAVE_DIR = os.path.join("D:", "小说", "凡人修仙传")
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.beqege.cc/"
}
# 预处理存储目录
os.makedirs(SAVE_DIR, exist_ok=True)
def sanitize_filename(title):
"""清理非法文件名字符"""
return re.sub(r'[\\/*?:"<>|]', "", title).strip()
for chapter_num in range(1, 2456):
# 智能延迟控制
time.sleep(random.uniform(0.8, 2.5))
try:
# 带重试的请求
for retry in range(3):
try:
response = cffi_requests.get(
BASE_URL.format(chapter_num),
impersonate='chrome110',
headers=HEADERS,
timeout=15,
verify=False
)
response.raise_for_status()
break
except Exception as e:
if retry == 2:
raise
time.sleep(2 ** retry)
# 精准内容解析
soup = BeautifulSoup(response.text, "lxml")
# 提取并清理标题
title_div = soup.find("div", class_="bookname")
chapter_title = title_div.h1.text.strip() if title_div else f"第{chapter_num}章"
# 提取并清理正文
content_div = soup.find("div", id="content")
if content_div:
# 移除广告内容
for ad in content_div.find_all(["div", "script", "a"]):
ad.decompose()
# 优化段落结构
paragraphs = [p.text.strip() for p in content_div.find_all("p") if p.text.strip()]
chapter_content = "\n".join(paragraphs)
else:
chapter_content = "内容获取失败"
# 生成安全文件名
safe_title = sanitize_filename(chapter_title)
file_path = os.path.join(SAVE_DIR, f"{chapter_num:04d}_{safe_title}.txt")
# 原子化写入
with open(file_path, "w", encoding="utf-8") as f:
f.write(f" {chapter_title} \n\n")
f.write(chapter_content)
print(f"成功保存:{chapter_num:04d} {chapter_title}")
except Exception as e:
print(f"章节 {chapter_num} 抓取失败: {str(e)}")
# 可在此记录失败章节用于重试
print("全部章节处理完成!")
关键技术突破:
-
智能命名系统:
def sanitize_filename(title): return re.sub(r'[\\/*?:"<>|]', "", title).strip() # 生成示例:0001_第一章.txt file_path = os.path.join(SAVE_DIR, f"{chapter_num:04d}_{safe_title}.txt")
-
三重内容防护:
-
广告标签移除(
decompose()
彻底清除DOM节点) -
段落有效性验证(
if p.text.strip()
过滤空段落) -
失败章节内容占位("内容获取失败"提示)
-
-
高级请求策略:
# 指数退避重试机制 for retry in range(3): try: # 请求逻辑 break except Exception: time.sleep(2 ** retry)
适用场景:需要分章节处理、建立小说数据库或进行章节级分析的情况
方案对比选型指南
特性 | 单文件版 | 分章节版 |
---|---|---|
文件数量 | 1 | 2000+ |
存储空间 | 较小 | 较大 |
容错能力 | 一般 | 优秀 |
章节定位 | 需全文搜索 | 直接文件访问 |
内容更新 | 需重新抓取 | 可增量更新 |
适合场景 | 终端阅读 | 数据分析 |