Scrapy中失败请求重试下载中间件的报错修复问题
问题修复方案
核心错误分析
- 中间件继承错误:下载中间件应继承
scrapy.downloadermiddlewares.DownloaderMiddleware而非Spider,继承Spider会导致中间件逻辑完全混乱。 process_response返回值违规:该方法不支持yield生成器,必须返回单个Response/Request对象,或抛出IgnoreRequest,原代码循环yield请求直接违反规则。- 状态码判断逻辑失效:
response.status是整数类型,原代码用字符串列表['401','403','410']判断,永远无法匹配。 - 重试逻辑设计错误:手动循环发送请求不符合Scrapy的请求调度机制,应利用框架原生的重试参数或
request.meta控制重试次数。 - 错误的Spider调用方式:直接实例化
XxxSpider()调用parse_event会绕过Scrapy引擎调度,无法正确处理响应。
修复后的代码实现
from scrapy import Request from scrapy.exceptions import IgnoreRequest class NegativeResponsesDownloaderMiddleware: # 定义需要拦截的错误状态码(整数类型) FILTERED_STATUSES = {401, 403, 410} def process_response(self, request, response, spider): # 获取配置的最大重试次数,默认3次 max_retry_times = spider.settings.getint('ERROR_HANDLING_ATTACK_RATE', 3) # 获取当前已重试次数,默认0 current_retry = request.meta.get('retry_times', 0) if response.status in self.FILTERED_STATUSES: spider.logger.warning(f"拦截错误状态码 {response.status} | URL: {response.url} | 已重试 {current_retry} 次") if current_retry < max_retry_times: # 复制原请求,保留headers、回调、cookies等参数 retry_request = request.copy() retry_request.meta['retry_times'] = current_retry + 1 retry_request.dont_filter = True # 避免被去重机制过滤 return retry_request else: # 达到最大重试次数,记录错误并终止请求 spider.logger.error(f"URL {response.url} 重试 {max_retry_times} 次仍失败 | 状态码: {response.status}") raise IgnoreRequest(f"URL {response.url} 重试次数耗尽") # 状态码正常,返回原响应给爬虫 return response
关键优化说明
- 遵循框架规则:通过
request.meta['retry_times']跟踪重试次数,返回单个重试请求,符合process_response的返回要求。 - 请求复用:用
request.copy()复制原请求,避免手动构造请求时丢失原有参数(如headers、回调函数)。 - 日志规范化:用Spider内置
logger替代print,便于Scrapy日志统一管理和排查问题。 - 去重控制:设置
dont_filter=True,确保重试请求不会被Scrapy的去重机制拦截。
额外配置建议
在settings.py中添加重试次数配置:
# 错误状态码的最大重试次数 ERROR_HANDLING_ATTACK_RATE = 3
如果需要更精细的重试策略(如不同状态码对应不同重试次数),可以结合Scrapy自带的RetryMiddleware进行扩展,无需完全自定义中间件。
内容的提问来源于stack exchange,提问作者avakado0
相关产品推荐
相关产品推荐

