首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >爬虫工程化:使用中间件在Scrapy中统一处理403状态码

爬虫工程化:使用中间件在Scrapy中统一处理403状态码

原创
作者头像
小白学大数据
发布2025-10-16 16:42:00
发布2025-10-16 16:42:00
1720
举报

一、 问题根源:为什么爬虫会遭遇403?

在构建解决方案之前,我们首先需要理解敌人。服务器返回403通常基于以下几点:

  1. User-Agent识别:服务器检测到请求来自非浏览器客户端(如Python-Requests、Scrapy),遂拒绝服务。
  2. IP频率限制:单个IP在单位时间内的请求频率过高,触发服务器的防爬虫策略。
  3. 缺少Referer头:对于某些通过链接跳转访问的资源,服务器会验证Referer头。
  4. Cookie或Session验证:需要特定登录状态或令牌的页面,匿名访问会被拒绝。
  5. 高级指纹检测:如TLS指纹、浏览器API支持等,这在Scrapy中相对少见,但在Selenium等驱动浏览器中更常见。

二、 解决方案:Scrapy下载器中间件

Scrapy的架构之美在于其高度的可扩展性。下载器中间件是位于Scrapy引擎和下载器之间的钩子框架,用于全局处理请求和响应。这正是我们统一处理403状态的理想场所。

我们的核心思路是:创建一个自定义中间件,捕获所有状态码为403的响应,并按照预设策略自动重试该请求,同时在重试前对请求进行“修饰”以绕过检测。

实现步骤与代码

我们将创建一个名为 Http403RetryMiddleware 的中间件。

步骤1:创建项目与中间件文件

假设你已经有一个Scrapy项目。如果没有,可以通过 scrapy startproject myproject 创建。我们在项目的 middlewares.py 文件中定义我们的中间件。

python

代码语言:javascript
复制
# middlewares.py

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

logger = logging.getLogger(__name__)

class Http403RetryMiddleware(RetryMiddleware):
    """
    自定义403重试中间件,继承自Scrapy内置的RetryMiddleware。
    这样我们可以复用其重试逻辑和设置(如重试次数、重试延迟)。
    """

    def process_response(self, request, response, spider):
        # 核心方法:处理响应
        # 如果响应状态码不是403,交给父类处理(处理429、500等)
        if response.status != 403:
            return super().process_response(request, response, spider)

        # 记录警告日志
        logger.warning(f"Intercepted 403 Forbidden for: {request.url}")

        # 调用重试方法
        reason = response_status_message(response.status)
        return self._retry(request, reason, spider) or response

    def process_request(self, request, spider):
        """
        在请求发送前,对其进行“修饰”,增加反反爬虫措施。
        这里会在每次重试(包括第一次)时被调用。
        """
        self._enhance_request(request)
        # 返回None,Scrapy会继续处理这个请求
        return None

    def _enhance_request(self, request):
        """
        增强请求,添加或修改请求头,模拟浏览器行为。
        """
        # 1. 设置一个常见的、真实的User-Agent池
        user_agent_list = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
        ]
        # 如果请求没有设置User-Agent,或者我们想要在重试时更换,可以在这里设置
        if not request.headers.get('User-Agent'):
            request.headers['User-Agent'] = random.choice(user_agent_list)

        # 2. 设置Referer头,可以设置为同域名下的一个安全页面,或者谷歌
        if not request.headers.get('Referer'):
            # 这里简单设置为同域名的根目录,实际项目中可以根据情况动态设置
            domain = request.url.split('/')[2] # 获取域名
            request.headers['Referer'] = f'https://{domain}/'

        # 3. 设置其他常见的请求头,使其更像浏览器
        request.headers.setdefault('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
        request.headers.setdefault('Accept-Language', 'zh-CN,zh;q=0.9,en;q=0.8')
        request.headers.setdefault('Accept-Encoding', 'gzip, deflate, br')
        request.headers.setdefault('DNT', '1')
        request.headers.setdefault('Connection', 'keep-alive')
        request.headers.setdefault('Upgrade-Insecure-Requests', '1')

        logger.debug(f"Enhanced request headers for: {request.url}")
步骤2:启用中间件并配置

仅仅创建中间件是不够的,我们需要在Scrapy项目的设置文件中启用它,并调整相关配置。

打开 settings.py 文件,进行如下修改:

python

代码语言:javascript
复制
# settings.py

# 下载器中间件配置
DOWNLOADER_MIDDLEWARES = {
    # 首先停用内置的RetryMiddleware,因为我们自定义的中间件继承了它并会替代其功能。
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
    # 然后添加我们自定义的403重试中间件,优先级数字可以自定义(500-600是常用范围)
    'myproject.middlewares.Http403RetryMiddleware': 550,

    # 其他中间件...
}

# 重试设置
# 总的重试次数(包括第一次请求)
RETRY_TIMES = 3

# 需要重试的HTTP状态码,确保403在其中
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429, 403] # 重点:加入了403

# 随机下载延迟,避免请求过于规律
DOWNLOAD_DELAY = 1
AUTOTHROTTLE_ENABLED = True # 推荐启用自动限速

# 并发请求数,根据目标网站承受能力调整
CONCURRENT_REQUESTS = 16

# 为特定网站设置自定义配置(可选)
CUSTOM_USER_AGENT = 'Mozilla/5.0 (compatible; MyBot/1.0; +http://mycompany.com)'
步骤3:在Spider中应用(可选高级技巧)

你还可以在Spider内部根据特定站点的需求,微调中间件的行为。例如,为某个特定的难啃的网站准备一个独立的User-Agent列表。

python

代码语言:javascript
复制
# spiders/__init__.py
# 假设我们在spider中设置了自定义的meta来指导中间件

class MySpider(scrapy.Spider):
    name = 'myspider'

    def start_requests(self):
        urls = ['https://example.com/page1', 'https://example-harder-site.com/page2']
        for url in urls:
            # 对于难处理的网站,可以设置一个标志,让中间件使用更激进的策略
            meta = {}
            if 'harder-site' in url:
                meta['use_aggressive_retry'] = True

            yield scrapy.Request(url=url, callback=self.parse, meta=meta)

    def parse(self, response):
        # ... 你的解析逻辑
        pass

然后,在中间件中,我们可以读取这个 meta 信息:

python

代码语言:javascript
复制
# 在 middlewares.py 的 process_request 方法中添加
def process_request(self, request, spider):
    # 检查spider的meta中是否有特殊指令
    if request.meta.get('use_aggressive_retry'):
        # 例如,更换一个不同的、更复杂的User-Agent池
        aggressive_agents = [...]
        request.headers['User-Agent'] = random.choice(aggressive_agents)
    else:
        self._enhance_request(request) # 使用默认的增强方法
    return None

三、 工程化优势与总结

通过上述实现,我们成功地将403错误处理工程化

  1. 统一处理:项目中所有Spider发出的请求,一旦遇到403,都会自动触发重试机制,无需在每个Spider中重复编写错误处理代码。
  2. 策略集中:所有反反爬虫的“修饰”逻辑(如更换User-Agent、添加Headers)都集中在中间件中,便于维护和更新。例如,当某个User-Agent失效时,只需在一个地方更新列表。
  3. 充分利用框架:继承自 RetryMiddleware,使我们能无缝接入Scrapy的重试、记录日志和统计系统。
  4. 灵活可扩展:该中间件可以轻松扩展以应对更复杂的情况,例如:
    • 集成代理IP:在 process_request 中,当重试次数超过一定阈值时,为请求设置代理 request.meta['proxy'] = some_proxy_url
    • 模拟登录:如果403是由于未登录引起的,可以在中间件中检查并触发一个登录流程。
    • 动态指纹应对:与更复杂的库(如 curl_cffiselenium)结合,在特定条件下使用它们来执行请求。

结论

在爬虫开发中,处理反爬虫机制不应是事后补救的散兵游勇,而应是项目初期就纳入设计的系统工程。利用Scrapy中间件对403状态码进行统一、智能化的处理,是迈向稳健、可维护、高效率的数据采集系统的关键一步。本文提供的方案不仅解决了403问题,更展示了一种工程化的思维模式,可举一反三应用于处理其他如429(请求过多)、JS挑战等复杂的爬虫挑战。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 问题根源:为什么爬虫会遭遇403?
  • 二、 解决方案:Scrapy下载器中间件
    • 实现步骤与代码
      • 步骤1:创建项目与中间件文件
      • 步骤2:启用中间件并配置
      • 步骤3:在Spider中应用(可选高级技巧)
  • 三、 工程化优势与总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档