You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Scrapy爬虫中如何正确处理202状态码以等待页面加载完成?

Scrapy爬虫中如何正确处理202状态码以等待页面加载完成?

刚接触Scrapy爬虫遇到202状态码确实头疼——这个状态码表示服务器已经接受了你的请求,但目标内容还没准备好,直接拿响应肯定拿不到有效数据。我之前踩过类似的坑,给你分享几个亲测有效的处理方式,应该能解决你之前尝试没成功的问题:

一、用自定义重试中间件接管202的重试逻辑

Scrapy自带的重试中间件默认只处理5xx、408这类错误码,不会管202,所以我们可以自定义一个中间件,让它识别202并触发带延迟的重试。你之前尝试了get_retry_request但没成功,大概率是没把自定义中间件配置对,或者没给202设置正确的重试规则。

步骤如下:

  1. 在你的爬虫项目里新建/修改middlewares.py文件,写入自定义中间件:
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

class Custom202RetryMiddleware(RetryMiddleware):
    def process_response(self, request, response, spider):
        # 如果响应状态是202,触发重试
        if response.status == 202:
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        
        # 其他状态码走默认逻辑
        return super().process_response(request, response, spider)
  1. settings.py里替换默认的重试中间件,启用你自定义的这个:
DOWNLOADER_MIDDLEWARES = {
    'your_project_name.middlewares.Custom202RetryMiddleware': 550,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
}
  1. 同时在settings.py里配置重试参数,把202加入重试的HTTP码列表:
RETRY_TIMES = 5  # 最大重试次数
RETRY_DELAY = 3  # 基础重试延迟(秒)
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 202]  # 加入202
RETRY_BACKOFF = 2  # 指数退避系数,比如第一次3秒,第二次6秒,第三次12秒...

这样配置后,遇到202状态码时,中间件会自动按照你设置的延迟和次数重试,不用在回调里写一堆重复代码。

二、在回调函数里手动处理202(适合需要灵活逻辑的场景)

如果你的需求更特殊——比如要根据响应头里的Retry-After字段动态设置延迟(很多服务器会在202响应里返回这个字段,告诉你多久后再重试),那手动在回调里处理会更灵活。你之前用reactor.callLater没成功,可能是没加dont_filter=True导致重复URL被Scrapy过滤了,或者没控制好重试次数。

示例代码如下:

from twisted.internet import reactor
from scrapy import Request

class YourSpider(scrapy.Spider):
    name = 'your_spider'
    max_retries = 5  # 自定义最大重试次数

    def parse(self, response):
        # 先判断状态码
        if response.status == 202:
            current_retries = response.meta.get('retries', 0)
            
            # 超过最大重试次数就放弃
            if current_retries >= self.max_retries:
                self.logger.warning(f'超过最大重试次数,放弃URL: {response.url}')
                return
            
            # 优先从响应头取推荐的重试延迟,没有的话用默认值
            retry_after = response.headers.get('Retry-After')
            delay = int(retry_after) if retry_after else 3
            
            self.logger.info(f'收到202状态码,{delay}秒后重试,当前重试次数: {current_retries+1}')
            
            # 用reactor调度延迟请求,注意设置dont_filter=True避免被过滤
            reactor.callLater(
                delay,
                self.crawler.engine.crawl,
                Request(
                    url=response.url,
                    callback=self.parse,
                    meta={
                        'location_name': response.meta.get('location_name', ''),
                        'retries': current_retries + 1
                    },
                    dont_filter=True  # 关键!重复URL不会被过滤
                )
            )
            return
        
        # 状态码正常,处理页面内容
        # ... 你的解析逻辑 ...

这里要注意几个关键点:

  • 一定要加dont_filter=True,因为Scrapy默认会过滤重复的URL请求,不加的话延迟后的请求会被直接丢弃
  • meta记录当前重试次数,避免无限重试导致死循环
  • 优先使用响应头里的Retry-After值,这是服务器推荐的等待时间,比固定延迟更合理

三、常见问题排查(你之前尝试失败的可能原因)

  1. get_retry_request时,有没有正确传递max_retry_times?比如要确保self.max_retries是在spider里定义好的,或者从settings的RETRY_TIMES
  2. 手动调度请求时,是不是没加dont_filter=True?这个是很多新手容易踩的坑
  3. 重试延迟是不是设置得太短?有些服务器需要更长的时间生成内容,延迟太短的话重试还是会拿到202

最后提个小建议

处理202时,不要把重试次数设置得太高、延迟设置得太短,不然既会给目标服务器造成不必要的压力,也可能导致你的IP被封。尽量遵循服务器返回的Retry-After提示,友好爬取才是长久之计~

火山引擎 最新活动