Scrapy爬虫中如何正确处理202状态码以等待页面加载完成?
Scrapy爬虫中如何正确处理202状态码以等待页面加载完成?
刚接触Scrapy爬虫遇到202状态码确实头疼——这个状态码表示服务器已经接受了你的请求,但目标内容还没准备好,直接拿响应肯定拿不到有效数据。我之前踩过类似的坑,给你分享几个亲测有效的处理方式,应该能解决你之前尝试没成功的问题:
一、用自定义重试中间件接管202的重试逻辑
Scrapy自带的重试中间件默认只处理5xx、408这类错误码,不会管202,所以我们可以自定义一个中间件,让它识别202并触发带延迟的重试。你之前尝试了get_retry_request但没成功,大概率是没把自定义中间件配置对,或者没给202设置正确的重试规则。
步骤如下:
- 在你的爬虫项目里新建/修改
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)
- 在
settings.py里替换默认的重试中间件,启用你自定义的这个:
DOWNLOADER_MIDDLEWARES = { 'your_project_name.middlewares.Custom202RetryMiddleware': 550, 'scrapy.downloadermiddlewares.retry.RetryMiddleware': None, }
- 同时在
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值,这是服务器推荐的等待时间,比固定延迟更合理
三、常见问题排查(你之前尝试失败的可能原因)
- 用
get_retry_request时,有没有正确传递max_retry_times?比如要确保self.max_retries是在spider里定义好的,或者从settings的RETRY_TIMES取 - 手动调度请求时,是不是没加
dont_filter=True?这个是很多新手容易踩的坑 - 重试延迟是不是设置得太短?有些服务器需要更长的时间生成内容,延迟太短的话重试还是会拿到202
最后提个小建议
处理202时,不要把重试次数设置得太高、延迟设置得太短,不然既会给目标服务器造成不必要的压力,也可能导致你的IP被封。尽量遵循服务器返回的Retry-After提示,友好爬取才是长久之计~




