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

关于scrapy模块中间件的简单理解

摘要

Scrapy爬虫模块是爬虫程序员使用最多的一个模块,它以快速,高层次等优势,深受爬虫工作者的喜爱,其中Scrapy的中间件功能也极其重要。中间件处于引擎和爬虫之间的钩子框架,允许开发者自定义处理请求和响应的过程。常用于用户代理,重试机制,请求头等。、

钩子框架

钩子框架是一种软件设计模式,允许程序在特定的点(钩子点)插入自定义代码。通过这些钩子点,开发者可以在不修改核心的代码的前提下扩展或修改程序的行为。(像不像python 中的装饰器)!钩子框架在插件系统,事件处理,拦截器等场景中非常常见。

在Scrapy模块中主要可以将中间件分为两类:

下载器中间件(Downloader Middleware): 处理请求和响应,与下载器交互。

爬虫中间件(Spider Middleware): 处理从下载器传递到爬虫的响应以及从爬虫输出的请求。

下载器中间件:

在下载器中间件(Downloader Middleware)中主要有以下方法:

process_request(request, spider)

process_request(request, spider): 在请求发送到下载器之前调用。基于这个原理,我们可以自定义头部,cookie, 代理ip,用户认证等信息添加上

1,添加自定义头部信息

为每一个请求添加一个自定义的头部信息

 def process_request(self, request, spider):# 在请求发送之前添加一个自定义头部request.headers['X-Custom-Header'] = 'CustomValue'return None

2, 设置代理

为请求设置代理服务器

class ProxyMiddleware:def process_request(self, request, spider):request.meta['proxy'] = "http://your_proxy_server:port"return None

3,模拟用户登录(cookie)

在请求中添加cookie模拟用户登录

class LoginMiddleware:def process_request(self, request, spider):request.cookies['sessionid'] = 'your_session_id'return None

4, 返回缓存响应

如果需要,可以直接返回一个缓存的响应,而不发送实际请求

from scrapy.http import HtmlResponseclass CacheMiddleware:def process_request(self, request, spider):if some_condition_based_on_request(request):return HtmlResponse(url=request.url, body='Cached response', encoding='utf-8', request=request)return None

启用中间件

要使中间件生效,需要在settings.py中启用它

process_response(request, response, spider)

process_response(request, response, spider):用于在下载器接收到响应后进行处理。通过这个方法,我们可以修改响应对象,执行日志记录,处理错误操作

方法签名

def process_response(self, request, response, spider):# 这里编写处理逻辑return response  # 或返回一个新的 Response 对象,或者抛出一个 IgnoreRequest 异常

参数说明

request :scrapy.http.Request对象,表示对应的请求

response:scrapy.http.Response对象,表示下载器接收到的响应

spider:当前处理响应的爬虫实例,便于访问爬虫的属性方法

返回值

response对象:可以使传入的响应对象,也可以是一个新的响应对象。如果返回新的响应对象,那么原有的响应将被替换。

request对象:可以返回一个新的请求对象,重新调度新的请求。

IgnoreRequest异常:可以抛出scrapy.exceptions.IgnoreRequest异常,忽略该请求

1,修改响应内容

可以对响应的内容进行修改,例如添加自定义标记或替换某些内容(例如给爬取的数据加上时间戳)

class ModifyResponseMiddleware:def process_response(self, request, response, spider):# 假设我们希望在响应体中添加一个自定义标记modified_body = response.body + b'\n<!-- Custom Footer -->'return response.replace(body=modified_body)

2, 日志记录

记录响应的状态码,方便调试和监控:

class LogResponseMiddleware:def process_response(self, request, response, spider):spider.logger.info(f'Response received from {request.url} with status {response.status}')return response

3,重试处理

根据响应状态码决定是否进行重试,例如对于某些特定的错误状态码进行重试:

from scrapy.exceptions import IgnoreRequestclass RetryMiddleware:def process_response(self, request, response, spider):if response.status in [500, 502, 503, 504]:spider.logger.warning(f'Retrying {request.url} due to server error {response.status}')return request  # 重新调度请求以进行重试return response

4, 缓存响应

将响应缓存到本地文件,避免重复下载:

import os
from scrapy.http import HtmlResponseclass CacheResponseMiddleware:def process_response(self, request, response, spider):cache_dir = 'cache'os.makedirs(cache_dir, exist_ok=True)cache_path = os.path.join(cache_dir, f"{hash(request.url)}.html")with open(cache_path, 'wb') as f:f.write(response.body)return responsedef process_request(self, request, spider):cache_path = os.path.join('cache', f"{hash(request.url)}.html")if os.path.exists(cache_path):with open(cache_path, 'rb') as f:body = f.read()return HtmlResponse(url=request.url, body=body, encoding='utf-8', request=request)return None

最后要在setting中启用这些中间件

process_exception(request, exception, spider)

process_exception(request, exception, spider):用于处理在爬取过程中发生的异常,当请求处理过程中出现异常时,Scrapy将调用该方法,并将相关请求,异常和爬虫对象作为参数传递给该方法。

参数说明

request:发生异常的请求对象

exception: 抛出的异常对象

spider: 当前爬虫对象

使用process_exception()方法可以根据具体的需求来处理异常,记录日志,重新发送请求,忽略异常等,以下是一个代码示例:

class MyMiddleware(object):def process_exception(self, request, exception, spider):# 处理异常的逻辑print(f"An exception occurred while processing {request.url}: {exception}")# 返回None表示继续处理异常,返回Response对象表示忽略异常并返回自定义的响应return None

在这个示例中,我们打印了异常的详细信息,并返回了None,表示继续处理异常。如果要忽略异常并返回自定义响应,可以通过返回一个Response对象来实现。

爬虫中间件:

爬虫中间件主要包括以下方法:

  • process_spider_input(response, spider):在爬虫处理响应之前调用。
  • process_spider_output(response, result, spider):在爬虫处理完响应之后调用。
  • process_spider_exception(response, exception, spider):在爬虫处理响应过程中抛出异常时调用。
  • process_start_requests(start_requests, spider):处理爬虫产生的初始请求。

我们一一介绍

process_spider_input(response, spider):

process_spider_input(response, spider)用于在爬虫处理响应数据之前对响应进行预处理。这个方法通常被用来对响应进行一些定制化的操作,例如修改响应内容,添加额外的字段或者进行其他预处理操作。

当爬虫处理一个响应时,scrapy会调用每个下载中间件的process_spider_input()方法,以便对响应进行处理,这个方法可以在自定义的下载中间件中实现,来响应进行定制化处理。

以下是process_spider_input()方法的参数说明:

response:要处理的响应对象。

spider:当前爬虫对象

如何在自定义下载中间件中实现process_spider_input()方法:

class MyMiddleware(object):def process_spider_input(self, response, spider):# 对响应进行处理的逻辑if 'special_field' not in response.meta:response.meta['special_field'] = 'special_value'return response

在这个实例中,在process_spider_input()方法中向响应的meta信息中添加一个特使字段,如果这个字段不存在的话。然后返回了处理后的响应对象。这样在爬虫处理响应数据之前,我们就可以对响应进行一些自定义的操作。

process_spider_output(response, result, spider)

process_spider_output(response, result, spider) :用于在爬虫生成的结果(如item, Request等)返回之前对其进行处理。这个方法通常被用在自定义的爬虫中间件中,以便对爬虫生成的结果进行筛选,修改或扩展。

该方法在爬虫处理完一个响应后,生成结果会依次传递给每个爬虫中间件的process_spider_output()方法,这样就可以在结果返回给引擎之情对其进行处理。

以下是process_spider_output()方法的参数说明:

response:处理过的响应对象

result:爬虫返回的结果,通常是包含一个item,request或者其他对象的可迭代对象(如生成器,列表等)

spider:当前爬虫对象。

下面一个简单的代码,展示如何在自定义爬虫中间件中实现process_spider_output方法:

class MySpiderMiddleware(object):def process_spider_output(self, response, result, spider):# 对爬虫输出的结果进行处理的逻辑for item in result:if isinstance(item, dict):  # 假设我们只对Item进行处理item['processed_by'] = 'MySpiderMiddleware'yield item

在这个实例中,process_spider_output()方法逐一处理爬虫生成的结果。如果结果是一个字典(假设它是一个item),则在其中添加一个字段processed_by,然后再将结果通过yield返回,这种方式可以确保所有生成的结果都被处理返回。

需要注意的是,process_spider_output()方法应该返回一个可迭代对象(通常使用yield来逐个返回结果)。如果多个中间件实现这个方法,它们将在设置中的顺序依次被调用,对结果进行链式处理。记得在中间件中启用。

process_spider_exception(response, exception, spider)

process_spider_exception(response, exception, spider):用于爬虫处理响应时发生异常时,对该异常进行处理。这个方法通常被用在自定义的爬虫中间件中,以便在异常发生时执行特定的逻辑,例如日志记录,错误处理或重试等操作。

方法参数:

response:导致异常的响应对象

exception:抛出的异常对象

spider:当前的爬虫对象

返回值

可以返回None或者一个None值的可迭代对象以继续传播异常或者可以返回一个新的result(如Item或Request对象)的可迭代对象在替代异常。

class MySpiderMiddleware(object):def process_spider_exception(self, response, exception, spider):# 处理异常的逻辑spider.logger.error(f"Exception caught: {exception} for URL: {response.url}")# 如果需要,可以生成新的Request以重试请求if isinstance(exception, SomeSpecificException):new_request = response.request.copy()new_request.dont_filter = True  # 不要因为重复请求而被过滤return [new_request]# 返回None以继续传播异常return None

在这个实例中,当爬虫处理响应时发生异常时,process_spider_exception()方法会:

1,记录异常信息和相关的URL

2, 如果异常类型为SomeSpecificException,则生成一个新的请求以重试该请求,并返回这个请求。

3,如果不是这个特定的异常类型,则返回None,继续传播异常

为了让Scrapy使用这个中间件,需要在项目的设置文件settings.py中启用它

process_start_requests(start_requests, spider)

process_start_requests(start_requests, spider): 用于在开始爬取时对起始请求进行处理。这个方法通常被用在自定义的爬虫中间件中,以便对起始请求进行自定义操作,例如添加额外的请求参数,修改请求头部。这和下载器中间件的process_request(request, spider)方法类似。

方法参数

start_requests:起始请求的可迭代对象。

spider:当前的爬虫对象

返回值

必须返回一个可迭代对象,该对象包含要发送的起始请求。

下面是一个示例代码,展示了如何在自定义爬虫中间件中实现process_start_requests()方法

class MySpiderMiddleware(object):def process_start_requests(self, start_requests, spider):# 对起始请求进行处理的逻辑for request in start_requests:# 添加额外的请求参数request.meta['extra_param'] = 'some_value'# 修改请求头部request.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'yield request

在这个示例中,process_start_request()方法会遍历起始请求,并对每个请求执行以下操作:

1,添加名为extra_param的额外参数请求,值为“some_value”

2,修改请求的头部,将User_Agent修改为指定的值

3,通过yield返回修改请求。

记得在settings.py文件汇总启用它

相关文章:

  • 案例分享:高科技企业产品管理部门设置和产品经理岗位设置
  • MySQL 面试突击指南:核心知识点解析1
  • YOLOv10改进 | 注意力篇 | YOLOv10引入YOLO-Face提出的SEAM注意力机制优化物体遮挡检测
  • 改进YOLOv7 | 在 ELAN 模块中添加【Triplet】【SpatialGroupEnhance】【NAM】【S2】注意力机制 | 附详细结构图
  • 浏览器组成的介绍
  • QT中QSettings的使用系列之二:保存和恢复应用程序主窗口
  • bash: nvcc: command not found
  • 查看 MAC 的 shell 配置文件
  • 2024考古之还在用原始JDBC开发 手搓 案例 实现一个模块的增删改
  • 基于51单片机的篮球计分器设计
  • 武汉工程大学24计算机考研数据,有学硕招收调剂,而专硕不招收调剂!
  • 【C++题解】1741 - 求出1~n中满足条件的数的个数和总和?
  • 国产芯片方案/血氧仪方案SIC88336
  • 音乐界的颠覆与挑战分析
  • UE/C++简单功能实现笔记
  • “大数据应用场景”之隔壁老王(连载四)
  • 230. Kth Smallest Element in a BST
  • Go 语言编译器的 //go: 详解
  • IndexedDB
  • Puppeteer:浏览器控制器
  • vue总结
  • 第2章 网络文档
  • 关于Java中分层中遇到的一些问题
  • 基于webpack 的 vue 多页架构
  • 基于游标的分页接口实现
  • 码农张的Bug人生 - 见面之礼
  • 排序(1):冒泡排序
  • 无服务器化是企业 IT 架构的未来吗?
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 7行Python代码的人脸识别
  • mysql面试题分组并合并列
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • # linux从入门到精通(三)
  • # 透过事物看本质的能力怎么培养?
  • #控制台大学课堂点名问题_课堂随机点名
  • $ git push -u origin master 推送到远程库出错
  • (1)bark-ml
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (CVPRW,2024)可学习的提示:遥感领域小样本语义分割
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (六)DockerCompose安装与配置
  • (三) diretfbrc详解
  • (十)Flink Table API 和 SQL 基本概念
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (一)Dubbo快速入门、介绍、使用
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)Linux下编译安装log4cxx
  • (转)Oracle存储过程编写经验和优化措施
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)