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

摘要

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

钩子框架

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

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

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

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

下载器中间件:

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

process_request(request, spider)

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

1,添加自定义头部信息

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

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

2, 设置代理

为请求设置代理服务器

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

3,模拟用户登录(cookie)

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

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

4, 返回缓存响应

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

python 复制代码
from scrapy.http import HtmlResponse

class 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):用于在下载器接收到响应后进行处理。通过这个方法,我们可以修改响应对象,执行日志记录,处理错误操作

方法签名

python 复制代码
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,修改响应内容

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

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

2, 日志记录

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

python 复制代码
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,重试处理

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

python 复制代码
from scrapy.exceptions import IgnoreRequest

class 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, 缓存响应

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

python 复制代码
import os
from scrapy.http import HtmlResponse

class 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 response

    def 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()方法可以根据具体的需求来处理异常,记录日志,重新发送请求,忽略异常等,以下是一个代码示例:

python 复制代码
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()方法:

python 复制代码
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方法:

python 复制代码
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对象)的可迭代对象在替代异常。

python 复制代码
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()方法

python 复制代码
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文件汇总启用它

相关推荐
阿昌喜欢吃黄桃7 天前
RocketMq事务消息原理
java·中间件·消息队列·rocketmq·mq
半夜修仙8 天前
延迟队列的介绍及常见问题
java·数据库·中间件·rabbitmq
手握风云-8 天前
一条消息的旅程:RabbitMQ 学习与实践(一)
中间件·rabbitmq
RH2312118 天前
2026.6.8Linux
java·数据库·中间件
理人综艺好会9 天前
双Token机制在实际项目中的应用与实践
中间件·token
番茄去哪了9 天前
神领物流面试题(一)
java·大数据·中间件
念何架构之路10 天前
消息中间件
中间件
都说名字长不会被发现10 天前
Spring Boot Starter 中间件账号密码加密方案设计与实现
java·spring boot·后端·中间件
瀚高PG实验室10 天前
java中间件无法连接数据库
java·数据库·中间件·瀚高数据库
之歆10 天前
Day11_Express 深入解析:从中间件到项目实战
中间件·express