简单爬虫框架实现

article/2025/6/7 13:41:26

1. 框架功能概述

(1) HttpSession 类:请求管理
  • 功能:封装 requests 库,实现带重试机制的 HTTP 请求(GET/POST)。
  • 关键特性
    • 自动处理 429(请求过多)、5xx(服务器错误)等错误,最多重试 3 次。
    • 自动设置请求头(User-AgentAccept 等),降低被网站封禁的风险。
    • 自动检测响应编码(response.encoding = response.apparent_encoding)。
(2) DataParser 类:数据解析
  • 功能:统一解析接口,支持三种解析方式:
    • XPath:使用 lxml 库,适合复杂层级结构解析。
    • CSS 选择器:使用 BeautifulSoup,适合快速定位元素。
    • 正则表达式:处理非结构化数据(如 JavaScript 生成的内容)。
  • 使用示例
    parser = DataParser(response)
    titles = parser.css(".title")         # CSS选择器
    links = parser.xpath('//a/@href')      # XPath
    numbers = parser.regex(r'\d+')         # 正则表达式
    
(3) Spider 基类:爬虫流程模板
  • 功能:定义爬虫的通用流程,强制子类实现核心逻辑。
  • 关键方法
    • start_requests():生成初始请求 URL 列表(子类必须实现)。
    • parse(response):解析页面数据(子类必须实现)。
    • run():主流程控制,包括请求发送、解析、数据保存和防封禁延迟。
  • 模板方法模式:子类只需聚焦业务逻辑(如 URL 生成和数据提取),框架自动处理请求重试、解析器初始化等通用逻辑。

2. 如何使用该框架?

步骤 1:创建类爬虫

新建一个 Python 文件(如 spider_framework.py)

'''
@Author  : 小宇
@File    : spider_framework.py
'''import re
import time
import requests
from bs4 import BeautifulSoup
from lxml import etree
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry# ---------------------------
# 全局配置(请求头)
# ---------------------------
DEFAULT_HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/114.0.0.0 Safari/537.36","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
}# ---------------------------
# 请求类(带重试机制)
# ---------------------------
class HttpSession:"""负责发送HTTP请求,支持失败重试"""def __init__(self, retries=3, timeout=10):self.session = requests.Session()self.timeout = timeout  # 请求超时时间(秒)# 配置重试策略(遇到429/5xx错误时重试)retry_strategy = Retry(total=retries,  # 最大重试次数backoff_factor=1,  # 重试间隔(秒)status_forcelist=[429, 500, 502, 503, 504])self.session.mount("http://", HTTPAdapter(max_retries=retry_strategy))self.session.mount("https://", HTTPAdapter(max_retries=retry_strategy))def get(self, url, headers=None):"""发送GET请求"""headers = headers or DEFAULT_HEADERS  # 使用默认请求头try:response = self.session.get(url, headers=headers, timeout=self.timeout)response.raise_for_status()  # 失败时抛出异常response.encoding = response.apparent_encoding  # 自动识别编码return responseexcept Exception as e:print(f"GET请求失败: {url}, 错误: {str(e)}")return Nonedef post(self, url, headers=None, data=None):"""发送POST请求(简化版,实际项目可扩展)"""headers = headers or DEFAULT_HEADERStry:response = self.session.post(url, headers=headers, data=data, timeout=self.timeout)response.raise_for_status()response.encoding = response.apparent_encodingreturn responseexcept Exception as e:print(f"POST请求失败: {url}, 错误: {str(e)}")return None# ---------------------------
# 解析类(支持3种解析方式)
# ---------------------------
class DataParser:"""统一解析接口,接收响应后生成解析对象"""def __init__(self, response):self.text = response.text  # 页面文本self.soup = BeautifulSoup(self.text, "lxml")  # 使用BeautifulSoup解析self.html = etree.HTML(self.text)  # 使用lxml解析XPathdef xpath(self, pattern):"""用XPath提取数据,返回列表"""return self.html.xpath(pattern)def css(self, selector):"""用CSS选择器提取数据,返回列表"""return self.soup.select(selector)def regex(self, pattern, flags=re.S):"""用正则表达式提取数据,返回匹配列表"""return re.findall(pattern, self.text, flags=flags)# ---------------------------
# 爬虫基类(模板方法)
# ---------------------------
class Spider:"""爬虫基类,定义通用流程,子类需实现关键方法"""def __init__(self, name):self.name = name  # 爬虫名称self.http = HttpSession()  # 初始化请求对象self.parser = None  # 解析器对象def start_requests(self):"""生成初始请求URL列表(需子类实现)"""raise NotImplementedError("请在子类中定义起始URL")def parse(self, response):"""解析页面数据(需子类实现)"""raise NotImplementedError("请在子类中定义解析逻辑")def run(self):"""爬虫主流程"""print(f"启动爬虫: {self.name}")for url in self.start_requests():  # 遍历所有请求URLresponse = self.http.get(url)  # 发送请求if not response:  # 请求失败时跳过continueself.parser = DataParser(response)  # 创建解析器print(f"成功获取页面: {url}")data = self.parse(response)  # 解析数据if data:self.save_data(data)  # 保存数据time.sleep(1)  # 暂停1秒防封禁def save_data(self, data):"""保存数据(默认打印到控制台,可自定义)"""print(f"解析到数据: {data}")
步骤 2:创建子类爬虫
1:BeautifulSoup

新建一个 Python 文件(如 douban_BeautifulSoup.py),继承 Spider 基类并实现抽象方法:

'''
@Time    : 2025/6/3 13:49
@Author  : 小宇
@File    : douban_BeautifulSoup .py'''
from spider_framework import Spiderclass DoubanSpider(Spider):def __init__(self):super().__init__(name="豆瓣电影TOP250爬虫")self.base_url = "https://movie.douban.com/top250"def start_requests(self):"""生成多页 URL(每页 25 条数据)"""return [f"{self.base_url}?start={i*25}" for i in range(10)]def parse(self, response):"""解析电影列表页,提取电影信息"""movies = []for item in self.parser.css(".item"):  # 使用 CSS 选择器定位每个电影项title = item.select_one(".title").get_text(strip=True)       # 标题rating = item.select_one(".rating_num").text                 # 评分quote = item.select_one(".inq")?.text.strip() or "无引言"    # 引言(处理可能不存在的情况)cover_url = item.select_one(".pic a img")["src"]             # 封面链接movies.append({"标题": title,"评分": rating,"引言": quote,"封面": cover_url})return moviesif __name__ == "__main__":spider = DoubanSpider()spider.run()  # 启动爬虫
2:XPath
'''
@Time    : 2025/6/3 14:01
@Author  : 小宇
@File    : douban_XPath.py
'''from spider_framework import Spiderclass DoubanSpider(Spider):def __init__(self):super().__init__(name="豆瓣电影TOP250爬虫")self.base_url = "https://movie.douban.com/top250"def start_requests(self):"""生成多页URL(每页25条)"""return [f"{self.base_url}?start={i * 25}" for i in range(10)]def parse(self, response):movies = []# 使用 XPath 选择所有电影项for item in self.parser.xpath('//div[@class="item"]'):  # lxml 元素title = item.xpath('.//span[@class="title"]/text()')[0]rating = item.xpath('.//span[@class="rating_num"]/text()')[0]quote = item.xpath('.//span[@class="inq"]/text()')quote = quote[0] if quote else "无引言"# 使用 XPath 获取封面链接cover_url = item.xpath('.//div[@class="pic"]/a/img/@src')[0]movies.append({"标题": title,"评分": rating,"引言": quote,"封面": cover_url})return moviesif __name__ == "__main__":spider = DoubanSpider()spider.run()
步骤 2:运行爬虫

执行脚本,输出类似以下内容:
在这里插入图片描述

3. 框架扩展建议

(1) 数据存储扩展
  • 需求:默认 save_data 方法仅打印数据,实际应用中需保存到文件或数据库。
  • 示例:保存到 CSV
    import csvclass DoubanSpider(Spider):def __init__(self):super().__init__(name="豆瓣电影TOP250爬虫")self.filename = "douban_movies.csv"self.csv_header = ["标题", "评分", "引言", "封面"]with open(self.filename, "w", newline="", encoding="utf-8") as f:self.writer = csv.DictWriter(f, fieldnames=self.csv_header)self.writer.writeheader()def save_data(self, data):"""重写保存方法,写入 CSV 文件"""with open(self.filename, "a", newline="", encoding="utf-8") as f:self.writer.writerows(data)print(f"已保存 {len(data)} 条数据到 {self.filename}")
    
(2) 代理池集成
  • 需求:避免 IP 被封禁,添加代理轮换功能。
  • 修改 HttpSession
    class HttpSession:def __init__(self, retries=3, timeout=10, proxies=None):self.proxies = proxies  # 代理列表# ... 其他初始化代码 ...def get(self, url, headers=None):headers = headers or DEFAULT_HEADERStry:# 随机选择代理(示例:proxies = ["http://proxy1.com", "http://proxy2.com"])proxy = random.choice(self.proxies) if self.proxies else Noneresponse = self.session.get(url,headers=headers,timeout=self.timeout,proxies={"http": proxy, "https": proxy}  # 设置代理)# ... 其他请求逻辑 ...except Exception as e:# ... 错误处理 ...
    
(3) 异步请求优化
  • 需求:提升爬取效率,使用异步框架(如 aiohttp + asyncio)。
  • 说明:需重写 HttpSession 为异步版本,并修改 Spider.run() 为异步流程,适合大规模数据爬取。

4. 常见问题与解决方案

(1) 解析器混用导致的错误
  • 问题:误用 BeautifulSoup 对象调用 xpath 方法,或反之。
    item = parser.css(".item")[0]  # BeautifulSoup 对象
    cover_url = item.xpath('.//img/@src')  # 错误:BeautifulSoup 无 xpath 方法
    
  • 解决方案
    • 统一解析方式:要么全用 CSS 选择器(soup.select()),要么全用 XPath(html.xpath())。
    • 使用 DataParser 的对应方法:
      # 使用 CSS 选择器
      cover_url = item.select_one("img")["src"]  
      # 或使用 XPath(需通过 parser.html 获取 lxml 根节点)
      cover_url = self.parser.xpath('//div[@class="item"][1]/img/@src')[0]  
      
(2) 反爬机制应对
  • 现象:网站返回 403(禁止访问)或空白页面。
  • 解决方案
    • 增加请求头(如 RefererCookie)。
    • 调整 time.sleep(1) 间隔(如改为随机延迟 time.sleep(random.uniform(2, 5)))。
    • 使用代理池或轮换 User-Agent。
(3) 动态内容爬取
  • 现象:数据通过 JavaScript 生成,静态请求无法获取。
  • 解决方案
    • 使用 SeleniumPlaywright 等浏览器自动化工具。
    • 分析接口,直接请求后端 API(如通过浏览器开发者工具抓包)。

5. 框架优势总结

  • 模块化设计:请求、解析、流程控制分离,易于维护和扩展。
  • 防封禁机制:自动重试、请求头设置、延迟等待,提升爬取稳定性。
  • 多解析支持:灵活选择 CSS/XPath/正则,适应不同页面结构。
  • 代码复用性:基类定义通用逻辑,子类只需实现业务相关方法,减少重复代码。

通过该框架,可快速开发各类静态网页爬虫,后续可根据具体需求逐步添加反爬策略、数据存储、异步请求等高级功能。


http://www.hkcw.cn/article/QeRZaUipSY.shtml

相关文章

欢乐熊大话蓝牙知识14:用 STM32 或 EFR32 实现 BLE 通信模块:从0到蓝牙,你也能搞!

🚀 用 STM32 或 EFR32 实现 BLE 通信模块:从0到蓝牙,你也能搞! “我能不能自己用 STM32 或 EFR32 实现一个 BLE 模块?” 答案当然是:能!还能很帅! 👨‍🏭 前…

网络攻防技术六:拒绝服务攻击

文章目录 一、拒绝服务攻击概述1、按攻击目标分类2、按攻击方式分类3、按受害者类型分类4、按攻击是否直接针对受害者分类5、按属性分类6、按舞厅分类7、按攻击机制分类 二、剧毒包型拒绝服务攻击1、碎片攻击2、Ping of Death攻击(ICMP Bug攻击)3、Land攻击4、循环攻…

阿里云无影云桌面深度测评

阿里云无影桌面深度测评:解锁云端工作“新范式”的“未来之钥”! 在数字化浪潮席卷全球的2025年,远程办公与混合办公已不再是权宜之计,而是职场不可逆转的新常态。然而,如何确保员工无论身在何处,都能拥有…

R²AIN SUITE AI知识库助力中国制造业数字化转型

一、市场现状:理性增长中的结构性机遇 走进2025年的中国制造业车间,你会看到这样的矛盾图景:一边是机器人手臂精准焊接的火花四溅,另一边是老师傅对着五套不同系统的屏幕皱眉记录数据。这种割裂感正是当前数字化转型深水区的真实…

Java函数式编程(下)

四、实际应用 1. 数据统计分析 示例1:商品订单数据统计分析 package com.itheima.day4.analysis;import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.YearMonth; import java.ti…

Java 检查一条线是否与圆接触或相交(Check if a line touches or intersects a circle)

给定一个圆的圆心坐标、半径 > 1 的圆心坐标以及一条直线的方程。任务是检查给定的直线是否与圆相交。有三种可能性: 1、线与圆相交。 2、线与圆相切。 3、线在圆外。 注意:直线的一般方程是 a*x b*y c 0,因此输入中只给出常数 a、b、…

typescript的Interface和Type

类型别名和接口非常相似,在大多数情况下你可以在它们之间自由选择。 几乎所有的 interface 功能都可以在 type 中使用,关键区别在于不能重新开放类型以添加新的属性,而接口始终是可扩展的。 // window.ts.transpileModule(src, {}); 这是调…

(17)课36:窗口函数的例题:例三登录时间与连续三天登录,例四球员的进球时刻连续进球。

(89)例三登录时间 : 保留代码版本 : CREATE TABLE sql_8( user_id varchar(2), login_date date ); insert into sql_8(user_id,login_date) values(A,2024-09-02),(A,2024-09-03),(A,2024-09-04),(B,2023-11-25),(B,2023-12- 3…

go语言基础|slice入门

slice slice介绍 slice中文叫切片,是go官方提供的一个可变数组,是一个轻量级的数据结构,功能上和c的vector,Java的ArrayList差不多。 slice和数组是有一些区别的,是为了弥补数组的一些不足而诞生的数据结构。最大的…

CodeTop100 Day21

今天这三题是二叉树中简单的题了,但也算是常考题 61、对称二叉树101 class Solution {public boolean isSymmetric(TreeNode root) {return ismirror(root,root);}public boolean ismirror(TreeNode t1,TreeNode t2){if(t1null&&t2null){return true;}if(t…

机器学习——主成分分析(PCA)

一、PCA思想 1.1 PCA定义 PCA(Principal Component Analysis,主成分分析)是一种统计学方法,用于对数据进行降维处理。它通过线性变换将原始数据转换到一个新的坐标系统中,使得在这个新坐标系下,数据的方差…

ONLYOFFICE 与 LocalAI:在 Ubuntu 上搭建 AI 文档编辑环境

如果你希望在本地 Ubuntu 设备上使用 AI 模型编辑文档、电子表格和演示文稿,ONLYOFFICE 桌面编辑器 与 GPT4ALL 的搭配并不是唯一的选择。 还有另一个本地 AI 平台可供选择。它安装方便,并可以通过 AI 模型处理本地办公文件。这个平台叫做 LocalAI&…

【OCCT+ImGUI系列】011-Poly-Poly_Triangle三角形面片

Poly_Triangle 是什么? Poly_Triangle 是一个非常轻量的类,用于表示一个三角网格中的单个三角形面片。它是构成 Poly_Triangulation(三角网格对象)的基本单位之一。之后会写关于碰撞检测的相关文章,三角面片是非常重要…

分布式拜占庭容错算法——权益证明(PoS)算法详解

Java 实现权益证明(PoS)算法详解 一、PoS 核心机制 #mermaid-svg-8VcpJQY92Jtxe9m1 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8VcpJQY92Jtxe9m1 .error-icon{fill:#552222;}#mermaid-s…

Pycharm的使用技巧总结

目录 一、高效便捷的快捷键 二、界面汉化处理 1.设置 2.插件 3.汉化插件安装 三、修改字体大小、颜色 1.选择文件-设置 2.选择编辑器-配色方案-python 3.修改注释行颜色 4.修改编辑器字体颜色 一、高效便捷的快捷键 序号快捷键功能场景效果1Ctrl /快速注释/取消注释…

从 Docker 到 Containerd:Kubernetes 容器运行时迁移实战指南

一、背景 Kubernetes 自 v1.24 起移除了 dockershim,不再原生支持 Docker Engine,用户需迁移至受支持的 CRI 兼容运行时,如: Containerd(推荐,高性能、轻量级) CRI-O(专为 Kuberne…

基于 NXP + FPGA+Debian 高可靠性工业控制器解决方案

在工业系统开发中,**“稳定”**往往比“先进”更重要。设备一旦部署,生命周期动辄 5~10 年,系统重启或异常恢复成本高昂。 这时候,一套“值得托付”的软硬件组合,就显得尤为关键。 ✅ NXP —— 提供稳定、长期供货的工…

03.搭建K8S集群

K8S集群搭建的方式 目前主流的搭建k8s集群的方式有kubeadm、minikube、二进制包三种方式: kubeadm(本案例搭建方式) 是一个工具,用于快速搭建kubernetes集群,目前应该是比较方便和推荐的,简单易用 kubea…

Redis底层数据结构之快链表(QuickList)

QuickList基本结构 用一句话来说,QuickList是一个双端链表,每一个链表节点中存储的是ZipList,参照下面这张图可以更好地理解QuickList的结构组成: QuickList在Redis6.0中一共定义了6个结构体,分别为: quick…

Mac查看MySQL版本的命令

通过 Homebrew 查看(如果是用 Homebrew 安装的) brew info mysql 会显示你安装的版本、路径等信息。 你的终端输出显示:你并没有安装 MySQL,只是查询了 brew 中的 MySQL 安装信息。我们一起来看下重点: &#x1f9fe…