pin_drop当前位置:知识文库 ❯ 图文

Scrapy中间件详解 - 下载器与爬虫中间件完整指南

一、中间件概述

中间件(Middleware)是Scrapy框架中用于扩展和修改请求/响应处理流程的核心机制。Scrapy的中间件分为两大类:下载器中间件(Downloader Middleware)和爬虫中间件(Spider Middleware)。它们充当了Scrapy引擎与下载器/Spider之间的拦截器,可以在请求发送前和响应返回后插入自定义逻辑。

下载器中间件位于Scrapy引擎和下载器之间,主要用于设置代理IP、修改请求头、处理Cookie、重试请求、处理响应等场景。它是Scrapy中最常用的中间件类型。爬虫中间件位于引擎和Spider之间,主要用于处理Spider的输入和输出,如处理异常、过滤Item、修改请求等。

二、中间件核心方法与语法

下载器中间件通过三个核心方法来拦截和处理请求与响应:

代码示例

class MyDownloaderMiddleware:
    def process_request(self, request, spider):
        """处理请求 - 在请求发送到下载器之前调用"""
        # 可以修改请求头、设置代理等
        return None  # 返回None继续处理

    def process_response(self, request, response, spider):
        """处理响应 - 在下载器返回响应后调用"""
        # 可以检查响应内容、处理重试等
        return response  # 必须返回response或request

    def process_exception(self, request, exception, spider):
        """处理异常 - 当下载器或process_request抛出异常时调用"""
        # 可以处理超时、连接错误等
        return None  # 返回None继续处理其他异常处理器

三、中间件方法与返回值详解

下载器中间件方法

方法 必填 返回值 说明
process_request(request, spider) None/Response/Request/IgnoreRequest 处理请求,在请求发送前调用
process_response(request, response, spider) Response/Request/IgnoreRequest 处理响应,在响应返回后调用
process_exception(request, exception, spider) None/Response/Request 处理异常,当下载出错时调用

process_request 返回值含义

返回值 说明
None 继续处理,传给下一个中间件或下载器
Response对象 跳过下载器,直接将此响应返回给引擎
Request对象 停止当前处理,将Request重新放入调度器
IgnoreRequest异常 忽略该请求,不会调用process_exception

settings.py 配置方法

代码示例

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomUserAgentMiddleware': 400,
    'myproject.middlewares.ProxyMiddleware': 500,
    'myproject.middlewares.CustomRetryMiddleware': 550,
}

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.MySpiderMiddleware': 500,
}

四、完整代码示例

示例1:随机User-Agent中间件

每次请求随机选择一个User-Agent,模拟不同浏览器访问,是最基础的反反爬虫手段:

代码示例

import random

class RandomUserAgentMiddleware:
    """随机User-Agent中间件 - 每次请求使用不同的浏览器标识"""

    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Edge/120.0.0.0',
        ]

    def process_request(self, request, spider):
        user_agent = random.choice(self.user_agents)
        request.headers['User-Agent'] = user_agent
        spider.logger.debug(f'使用UA: {user_agent[:50]}...')
        return None

示例2:代理中间件

通过 request.meta['proxy'] 设置代理IP,并使用 from_crawler 从settings读取配置:

代码示例

class ProxyMiddleware:
    """代理中间件 - 为请求设置HTTP代理"""

    def __init__(self, proxy_url):
        self.proxy_url = proxy_url

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            proxy_url=crawler.settings.get('PROXY_URL', ''),
        )

    def process_request(self, request, spider):
        if self.proxy_url:
            request.meta['proxy'] = self.proxy_url
            spider.logger.debug(f'使用代理: {self.proxy_url}')
        return None

    def process_exception(self, request, exception, spider):
        spider.logger.warning(f'请求异常: {exception}')
        return None

示例3:自定义重试中间件

继承Scrapy内置的RetryMiddleware,自定义重试策略。当遇到5xx错误码或网络异常时自动重试,最多重试3次:

代码示例

from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

class CustomRetryMiddleware(RetryMiddleware):
    """自定义重试中间件 - 针对特定状态码和异常自动重试"""

    RETRY_TIMES = 3
    RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]

    def process_response(self, request, response, spider):
        # 检查响应状态码是否需要重试
        if response.status in self.RETRY_HTTP_CODES:
            retry_times = request.meta.get('retry_times', 0)
            if retry_times < self.RETRY_TIMES:
                spider.logger.info(
                    f'重试 {retry_times + 1}/{self.RETRY_TIMES}: '
                    f'{request.url} (状态码: {response.status})'
                )
                request.meta['retry_times'] = retry_times + 1
                request.dont_filter = True
                return request
        return response

    def process_exception(self, request, exception, spider):
        retry_times = request.meta.get('retry_times', 0)
        if retry_times < self.RETRY_TIMES:
            spider.logger.info(
                f'异常重试 {retry_times + 1}/{self.RETRY_TIMES}: {request.url}'
            )
            request.meta['retry_times'] = retry_times + 1
            request.dont_filter = True
            return request.copy()
        return None

示例4:Cookie池中间件

代码示例

import random

class CookiePoolMiddleware:
    """Cookie池中间件 - 随机使用Cookie池中的Cookie"""

    def __init__(self, cookie_list):
        self.cookie_list = cookie_list

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            cookie_list=crawler.settings.getlist('COOKIE_POOL', []),
        )

    def process_request(self, request, spider):
        if self.cookie_list:
            cookie = random.choice(self.cookie_list)
            request.cookies = cookie
            spider.logger.debug(f'使用Cookie: {cookie.get("session_id", "")[:10]}...')
        return None

五、中间件类型对比

类型 作用位置 典型用途 配置项
下载器中间件 Engine与Downloader之间 代理、UA、重试、Cookie DOWNLOADER_MIDDLEWARES
爬虫中间件 Engine与Spider之间 异常处理、Item过滤 SPIDER_MIDDLEWARES

六、实际应用场景

  • 场景1 - 反反爬虫:组合使用随机User-Agent、代理IP池和Cookie池中间件,使爬虫的请求特征更接近真实用户,绕过网站的反爬检测机制。

  • 场景2 - 请求重试:当遇到网络波动、服务器5xx错误或429(请求过快)时,自动重试请求,提高爬取成功率。可以自定义重试次数、间隔和触发条件。

  • 场景3 - 请求认证:对特定URL自动添加认证信息,如API Key、Token、Referer等。可以在 process_request 中根据URL模式判断并添加请求头。

小贴士

Scrapy默认启用了多个内置中间件,如 scrapy.downloadermiddlewares.retry.RetryMiddleware(优先级550)和 scrapy.downloadermiddlewares.redirect.RedirectMiddleware(优先级600)。在编写自定义中间件时,需要注意与内置中间件的优先级关系,确保执行顺序正确。可以使用 scrapy settings --get DOWNLOADER_MIDDLEWARES 查看当前启用的中间件列表。

七、注意事项与最佳实践

注意1:中间件优先级数字越小越先执行(process_request 顺序执行),但 process_response 是逆序执行的(数字大的先执行)。

注意2process_request 返回 None 才会继续下载,返回 Response 会跳过下载器直接返回给引擎。

注意3:代理设置通过 request.meta['proxy'] 传递,格式为 http://host:port。如果需要认证,使用 http://user:pass@host:port

注意4from_crawler 类方法是获取Scrapy配置的标准方式,通过它可以访问 crawler.settingscrawler.signals 等核心对象。

八、练习题

练习1

编写一个下载器中间件 RefererMiddleware,为每个请求添加 Referer 请求头。Referer的值从settings.py中的 DEFAULT_REFERER 配置项读取,使用 from_crawler 方法获取配置。

练习2

编写一个中间件 AutoProxyMiddleware,当响应状态码为403时,自动从代理池中更换代理IP并重试请求。代理池维护在settings.py的 PROXY_POOL 列表中,使用 process_response 方法实现。


九、常见问题FAQ

常见问题

下载器中间件和爬虫中间件有什么区别?

下载器中间件作用于请求和响应阶段,位于引擎与下载器之间,主要用于处理HTTP层面的逻辑(代理、UA、重试等);爬虫中间件作用于Spider阶段,位于引擎与Spider之间,主要用于处理Spider的输入输出(Item过滤、请求去重等)。日常开发中下载器中间件使用频率更高。

中间件的执行顺序是怎样的?

process_request按优先级从小到大执行(数字小的先执行),process_response则相反(数字大的先执行),process_exception也按从小到大执行。这种设计确保了请求和响应的处理形成"洋葱模型"。

如何在中间件中使用异步代理池API?

可以在 open_spider() 中初始化代理池连接,在 process_request() 中从池中获取代理。对于异步API,可以使用 twisted.internet.deferDeferred 对象来返回异步结果。

中间件中可以访问Spider的属性吗?

可以。所有中间件方法都接收 spider 参数,可以通过 spider.namespider.custom_attr 等访问Spider的属性和方法。也可以通过 spider.logger 记录日志。

如何禁用某个内置中间件?

settings.py 中将内置中间件的优先级设置为 None 即可禁用,例如:DOWNLOADER_MIDDLEWARES = {'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': None}

标签: Scrapy 中间件 代理IP User-Agent 反爬虫 Python爬虫 请求重试

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Scrapy Selector详解 - CSS与XPath数据提取完整指南 下一篇: Scrapy分页爬取详解 - 从简单分页到多级分页完整指南

poll相关推荐