前言在网络爬虫实际运行过程中网络波动、目标服务器限流、临时连接超时、状态码异常、页面临时跳转等问题频发直接导致单次请求采集失败。若未做容错处理失败请求会直接丢弃最终造成数据缺失、采集完整性下降。Scrapy 框架提供下载器中间件作为请求与响应的统一拦截层可在请求发送前、响应接收后、请求异常触发时介入处理是实现请求重试、请求修复、异常拦截的核心载体。本文基于前三篇完成的模块化 Scrapy 架构深度讲解 Scrapy 下载器中间件的运行机制、生命周期方法从零开发请求失败自动重试中间件覆盖网络异常重试、HTTP 异常状态码重试、超时重试、重试次数限制、重试间隔控制、日志统计等完整功能。同时结合工程化规范完成中间件分层部署、配置启用、异常分类处理方案可直接应用于线上生产环境有效提升爬虫稳定性与数据完整率。本文涉及核心工具与官方资源Scrapy 中间件官方文档下载器中间件 API、执行流程、内置中间件说明Python requests 异常体系网络异常类型参考Scrapy 异常类说明框架内置异常定义Twisted 网络异常文档底层网络连接异常分类一、Scrapy 中间件体系与重试业务场景分析1.1 Scrapy 中间件分类与执行流程Scrapy 框架内置两大核心中间件体系分工明确、执行链路不同结合项目架构做区分说明表格中间件类型存放目录作用范围核心生命周期典型应用场景下载器中间件middlewares/downloader_mw.py拦截请求、响应、网络异常作用于网络请求链路process_request、process_response、process_exception重试、UA 伪装、代理切换、请求超时、异常捕获爬虫中间件middlewares/spider_mw.py拦截Item、爬虫回调、爬虫异常作用于解析逻辑链路process_spider_input、process_spider_output数据预处理、Item 过滤、爬虫异常捕获、路由分发本次自动重试功能属于网络请求层容错因此基于下载器中间件开发。Scrapy 请求完整链路爬虫生成 Request 请求 → 下载器中间件逐层执行→ 下载器发送网络请求 → 接收 Response / 触发异常 → 下载器中间件回传处理 → 爬虫 parse 解析函数。所有网络层面的失败、超时、状态码异常都会进入process_exception方法也是重试逻辑的核心入口。1.2 爬虫请求失败常见类型结合线上爬虫运行经验将需要重试的失败场景划分为六大类也是中间件需要针对性处理的目标网络连接异常断网、连接被拒绝、连接重置、DNS 解析失败属于临时性网络问题请求超时异常目标服务器响应缓慢超出框架预设超时时间HTTP 服务端异常500、502、503、504 等服务端错误多为服务器临时故障、过载客户端临时限流429 请求过于频繁、408 请求超时站点临时限制访问页面临时跳转301/302 临时跳转导致原始页面数据丢失未知网络异常Twisted 底层抛出的未归类网络错误。而 404 页面不存在、401 未授权、403 永久封禁等永久性错误不建议重试重复请求无意义需直接丢弃请求并记录日志。1.3 自动重试核心设计指标为保证重试机制合理可控避免无限重试、加重目标服务器压力设定核心约束指标最大重试次数单条请求允许重试上限超出次数直接放弃重试延迟两次重试之间设置时间间隔避免高频轰炸服务器可重试异常 / 状态码白名单仅对指定异常、状态码执行重试重试日志统计记录每一次重试行为、失败原因、最终结果便于运维排查参数可配置化重试次数、延迟、白名单全部写入全局配置无需修改源码。1.4 模块部署规划基于现有模块化架构重试中间件统一写入sqlite_spider/middlewares/downloader_mw.py遵循目录规范所有重试参数统一托管至config/column_config.py实现配置解耦中间件启用开关、优先级在顶层settings.py中配置完全符合工程化标准。二、前置准备配置层新增重试全局参数首先将重试相关的所有可变参数抽离至配置文件实现参数统一管理。打开sqlite_spider/config/column_config.py在文件末尾追加重试配置项python运行# 爬虫自动重试配置 # 单请求最大重试次数 MAX_RETRY_TIMES 3 # 重试间隔时间单位秒 RETRY_DELAY 1.5 # 允许重试的HTTP状态码白名单 RETRY_STATUS_CODES {500, 502, 503, 504, 408, 429} # 框架内置需要重试的异常类型Twisted网络异常 RETRY_EXCEPTIONS ( twisted.internet.error.TimeoutError, twisted.internet.error.ConnectionRefusedError, twisted.internet.error.ConnectionDone, twisted.internet.error.TCPTimedOutError )同时在config/__init__.py中导出新增配置保证全项目可正常引用python运行from .column_config import ( SPIDER_COLUMN_CONFIG, NEXT_PAGE_SELECTOR, SQLITE_DB_PATH, MAX_RETRY_TIMES, RETRY_DELAY, RETRY_STATUS_CODES, RETRY_EXCEPTIONS ) from .settings_dev import * from .settings_prod import * # 环境标识切换此处即可切换整套配置 ENV dev依赖补充中间件需要引入 Twisted 异常类Python 环境已随 Scrapy 自动安装无需额外执行安装命令。三、下载器中间件生命周期方法详解在编写代码前明确下载器中间件三个核心方法的触发时机、入参、返回值与使用规则这是开发自定义中间件的基础。3.1 process_request 方法触发时机请求发送至下载器之前执行。入参request请求对象、spider当前爬虫实例返回值三种规则返回None请求正常向下传递进入下一层中间件或下载器返回Response对象直接截断请求链路将伪造响应回传给爬虫返回Request对象丢弃当前请求重新发起新请求。本次用途为请求对象附加自定义重试计数记录当前请求重试次数。3.2 process_response 方法触发时机下载器获取响应之后回传给爬虫之前执行。入参request、response、spider返回值规则必须返回Request或Response对象。本次用途判断响应状态码是否在重试白名单内若命中则生成重试请求。3.3 process_exception 方法触发时机请求发送过程中抛出异常时执行网络异常、超时、连接失败等。入参request、exception、spider返回值规则返回Request则触发重试返回None则终止请求。本次用途捕获网络异常判断异常类型是否允许重试执行重试逻辑。四、完整代码实现自动重试中间件开发打开sqlite_spider/middlewares/downloader_mw.py编写完整的重试中间件代码包含重试计数、状态码重试、异常重试、延迟控制、日志输出全功能代码附带逐行原理注释。4.1 重试中间件完整代码python运行import time from twisted.internet.error import TimeoutError, ConnectionRefusedError, ConnectionDone, TCPTimedOutError from scrapy.http import Request from sqlite_spider.config import ( MAX_RETRY_TIMES, RETRY_DELAY, RETRY_STATUS_CODES, RETRY_EXCEPTIONS ) class RequestRetryMiddleware: 下载器中间件请求失败自动重试中间件 功能网络异常、服务端状态码异常自动重试限制最大重试次数与重试间隔 def process_request(self, request, spider): 请求预处理初始化重试计数器 每一个新请求进入链路时绑定重试次数标记 # 仅首次请求初始化重试计数避免重复赋值 if not hasattr(request, retry_times): request.retry_times 0 return None def process_response(self, request, response, spider): 响应拦截处理HTTP状态码异常重试 针对5xx、429、408等临时错误执行重试 # 获取当前请求已重试次数 current_retry request.retry_times # 判断响应状态码是否在重试白名单中 if response.status in RETRY_STATUS_CODES: # 超出最大重试次数直接返回原始响应终止重试 if current_retry MAX_RETRY_TIMES: spider.logger.error( f请求状态码异常已达最大重试次数{MAX_RETRY_TIMES}次放弃重试 | fURL{request.url} 状态码{response.status} ) return response # 重试次数自增 request.retry_times 1 # 执行重试延迟降低请求频率 time.sleep(RETRY_DELAY) spider.logger.warning( f状态码异常触发重试当前重试次数{request.retry_times} | fURL{request.url} 状态码{response.status} ) # 生成新请求复用原有请求参数重新加入请求队列 retry_request request.copy() return retry_request # 正常状态码直接返回响应进入爬虫解析逻辑 return response def process_exception(self, request, exception, spider): 异常拦截处理网络层面异常重试 针对超时、连接失败、连接断开等网络异常执行重试 current_retry request.retry_times # 判断当前异常是否属于可重试异常 if isinstance(exception, RETRY_EXCEPTIONS): # 超出最大重试次数终止重试返回None丢弃请求 if current_retry MAX_RETRY_TIMES: spider.logger.error( f网络异常已达最大重试次数{MAX_RETRY_TIMES}次放弃重试 | fURL{request.url} 异常类型{type(exception).__name__} ) return None # 重试次数自增 request.retry_times 1 time.sleep(RETRY_DELAY) spider.logger.warning( f网络异常触发重试当前重试次数{request.retry_times} | fURL{request.url} 异常信息{str(exception)} ) # 复制原请求对象发起重试 retry_request request.copy() return retry_request # 非可重试异常直接丢弃请求 spider.logger.error(f不可重试异常 | URL{request.url} 异常{str(exception)}) return None4.2 代码核心原理深度解析重试计数器原理利用request对象动态绑定retry_times属性记录重试次数该属性跟随请求在中间件链路中传递每一次重试完成计数自增。使用hasattr判断属性是否存在保证仅在请求首次进入时初始化避免计数被重复清零。请求复制原理Scrapy 中request.copy()方法会完整复制原请求的 URL、请求头、Cookie、回调函数、元数据等所有属性保证重试请求与原始请求完全一致不会丢失业务参数。返回新的Request对象后框架会自动将其重新放入调度队列实现二次请求。重试延迟原理通过time.sleep()实现固定间隔等待作用是平滑请求频率防止短时间内多次请求对目标服务器造成压力同时提升网络恢复概率。延迟时间由全局配置统一控制可根据站点规则灵活调整。异常分类处理原理通过isinstance判断异常类型仅对白名单内的临时网络异常执行重试对于 404、403 等永久性错误、未知异常直接终止请求并打印错误日志避免无效循环重试。日志分层原理使用warning级别记录正常重试行为使用error级别记录重试失败、不可重试异常日志层级清晰线上运维可快速区分问题类型。五、中间件启用与全局配置中间件编写完成后必须在settings.py中手动启用同时配置执行优先级。打开项目顶层sqlite_spider/settings.py找到DOWNLOADER_MIDDLEWARES配置项添加重试中间件python运行# 导入环境配置 from sqlite_spider.config import * # 启用数据管道 ITEM_PIPELINES { sqlite_spider.pipelines.SqlitePipeline: 300, } # 下载器中间件配置键中间件完整路径值优先级数字越小执行顺序越靠前 DOWNLOADER_MIDDLEWARES { # 自动重试中间件优先级设置为100 sqlite_spider.middlewares.downloader_mw.RequestRetryMiddleware: 100, } # 爬虫中间件占位后续扩展使用 SPIDER_MIDDLEWARES {} # 自定义扩展占位 EXTENSIONS {} # 全局请求超时时间配合重试机制使用单位秒 DOWNLOAD_TIMEOUT 10配置补充说明优先级规则下载器中间件按照数字从小到大依次执行本次设置100属于高优先级保证重试逻辑优先拦截请求与响应DOWNLOAD_TIMEOUT全局请求超时时间当请求超过 10 秒未返回数据触发TimeoutError异常进入重试逻辑参数可根据业务调整原生 Scrapy 内置重试中间件默认关闭本次为自定义实现两者互不冲突。六、功能测试与场景验证6.1 常规启动测试进入项目根目录执行爬虫启动命令bash运行scrapy crawl book_spider正常网络环境下所有请求均可一次成功终端仅输出正常采集日志无重试相关警告日志证明中间件不影响正常业务流程。6.2 人为模拟网络异常测试模拟断网运行爬虫过程中断开网络连接终端会持续输出网络异常触发重试警告日志重试次数逐步累加当重试次数达到配置的3次上限后输出已达最大重试次数放弃重试错误日志请求终止。恢复网络后新请求正常采集。模拟服务端异常修改目标 URL 为临时返回 503 状态码的测试地址观察状态码重试逻辑验证计数、延迟、上限限制全部生效。6.3 数据完整性验证完成异常场景测试后打开data.db数据库查看数据。经过重试恢复的请求可正常解析并入库仅完全失败的请求缺失数据整体数据采集完整率大幅提升。七、功能优化与进阶扩展方案基于基础重试中间件结合生产环境需求提供多套优化方案可根据业务复杂度选择性迭代。7.1 优化一指数退避重试动态延迟固定延迟在网络恢复缓慢的场景下效果有限指数退避会让重试间隔逐次变长降低后期请求频率。修改延迟逻辑代码python运行# 原固定延迟time.sleep(RETRY_DELAY) # 指数退避第1次延迟1.5s第2次3s第3次6s delay RETRY_DELAY * (2 ** (current_retry - 1)) time.sleep(delay)该方案适合网络波动较大、恢复较慢的站点。7.2 优化二请求去重防循环重试部分页面跳转异常会导致请求无限循环结合 Scrapy 请求指纹机制在重试时增加 URL 去重判断避免同一 URL 反复重试。7.3 优化三区分栏目单独配置重试规则若不同栏目网络环境、反爬规则不同可在column_config.py中为每个栏目单独配置重试次数、延迟、状态码白名单中间件根据请求 URL 匹配对应栏目规则实现精细化控制。7.4 优化四重试数据统计与入库扩展中间件逻辑将重试次数、失败 URL、异常类型、最终结果写入 SQLite 统计表实现可视化统计便于长期运维监控。7.5 优化五结合 UA / 代理中间件联动在重试请求时同步切换 User-Agent 或代理 IP解决因 IP 封禁、UA 限制导致的请求失败实现重试伪装组合容错进一步提升通过率。八、中间件开发规范与线上运维注意事项8.1 中间件开发规范功能单一原则一个中间件文件内按功能划分类重试、UA、代理等功能拆分不同类禁止多类逻辑混合在同一个中间件配置解耦原则所有阈值、白名单、时间参数统一写入config目录禁止硬编码日志规范区分warning/error/info日志级别日志内容包含 URL、次数、异常类型三大核心字段异常捕获中间件内部增加全局try-except防止中间件自身报错导致整个爬虫崩溃。8.2 线上运维注意事项合理设置重试上限最大重试次数建议控制在 2~5 次次数过高会消耗系统资源区分可重试场景严格区分临时异常与永久异常不对 403、404、401 执行重试配合请求延迟使用全局DOWNLOAD_DELAY与重试延迟叠加使用严格遵守目标站点访问规则日志监控线上部署后重点监控重试错误日志高频重试 URL 代表站点访问异常及时排查 IP 封禁、反爬策略变更问题资源管控高并发爬虫场景下重试请求会增加调度队列压力需同步控制并发数。九、本章总结本文基于模块化架构完成了 Scrapy 下载器中间件的开发落地一套完整的请求失败自动重试机制。从中间件分类、生命周期方法、配置设计到代码编写、启用配置、场景测试完整覆盖了生产级重试功能的全流程开发。该重试中间件解决了网络波动、服务端临时故障、请求限流等常见问题显著提升爬虫的稳定性与数据采集完整度同时严格遵循项目模块化、配置解耦、日志标准化的工程化规范可无缝集成至现有项目并长期迭代。目前本系列已完成轻量化入库、多栏目适配、项目模块化、请求自动重试四大核心功能整套架构已具备线上基础运行能力。下一篇将在此架构之上讲解 Scrapy 项目打包、脚本封装、一键启动与全环境部署方案实现爬虫项目快速分发、批量部署。
Python Scrapy 爬虫实战进阶系列(四):中间件开发 实现请求失败自动重试与异常请求容错机制
前言在网络爬虫实际运行过程中网络波动、目标服务器限流、临时连接超时、状态码异常、页面临时跳转等问题频发直接导致单次请求采集失败。若未做容错处理失败请求会直接丢弃最终造成数据缺失、采集完整性下降。Scrapy 框架提供下载器中间件作为请求与响应的统一拦截层可在请求发送前、响应接收后、请求异常触发时介入处理是实现请求重试、请求修复、异常拦截的核心载体。本文基于前三篇完成的模块化 Scrapy 架构深度讲解 Scrapy 下载器中间件的运行机制、生命周期方法从零开发请求失败自动重试中间件覆盖网络异常重试、HTTP 异常状态码重试、超时重试、重试次数限制、重试间隔控制、日志统计等完整功能。同时结合工程化规范完成中间件分层部署、配置启用、异常分类处理方案可直接应用于线上生产环境有效提升爬虫稳定性与数据完整率。本文涉及核心工具与官方资源Scrapy 中间件官方文档下载器中间件 API、执行流程、内置中间件说明Python requests 异常体系网络异常类型参考Scrapy 异常类说明框架内置异常定义Twisted 网络异常文档底层网络连接异常分类一、Scrapy 中间件体系与重试业务场景分析1.1 Scrapy 中间件分类与执行流程Scrapy 框架内置两大核心中间件体系分工明确、执行链路不同结合项目架构做区分说明表格中间件类型存放目录作用范围核心生命周期典型应用场景下载器中间件middlewares/downloader_mw.py拦截请求、响应、网络异常作用于网络请求链路process_request、process_response、process_exception重试、UA 伪装、代理切换、请求超时、异常捕获爬虫中间件middlewares/spider_mw.py拦截Item、爬虫回调、爬虫异常作用于解析逻辑链路process_spider_input、process_spider_output数据预处理、Item 过滤、爬虫异常捕获、路由分发本次自动重试功能属于网络请求层容错因此基于下载器中间件开发。Scrapy 请求完整链路爬虫生成 Request 请求 → 下载器中间件逐层执行→ 下载器发送网络请求 → 接收 Response / 触发异常 → 下载器中间件回传处理 → 爬虫 parse 解析函数。所有网络层面的失败、超时、状态码异常都会进入process_exception方法也是重试逻辑的核心入口。1.2 爬虫请求失败常见类型结合线上爬虫运行经验将需要重试的失败场景划分为六大类也是中间件需要针对性处理的目标网络连接异常断网、连接被拒绝、连接重置、DNS 解析失败属于临时性网络问题请求超时异常目标服务器响应缓慢超出框架预设超时时间HTTP 服务端异常500、502、503、504 等服务端错误多为服务器临时故障、过载客户端临时限流429 请求过于频繁、408 请求超时站点临时限制访问页面临时跳转301/302 临时跳转导致原始页面数据丢失未知网络异常Twisted 底层抛出的未归类网络错误。而 404 页面不存在、401 未授权、403 永久封禁等永久性错误不建议重试重复请求无意义需直接丢弃请求并记录日志。1.3 自动重试核心设计指标为保证重试机制合理可控避免无限重试、加重目标服务器压力设定核心约束指标最大重试次数单条请求允许重试上限超出次数直接放弃重试延迟两次重试之间设置时间间隔避免高频轰炸服务器可重试异常 / 状态码白名单仅对指定异常、状态码执行重试重试日志统计记录每一次重试行为、失败原因、最终结果便于运维排查参数可配置化重试次数、延迟、白名单全部写入全局配置无需修改源码。1.4 模块部署规划基于现有模块化架构重试中间件统一写入sqlite_spider/middlewares/downloader_mw.py遵循目录规范所有重试参数统一托管至config/column_config.py实现配置解耦中间件启用开关、优先级在顶层settings.py中配置完全符合工程化标准。二、前置准备配置层新增重试全局参数首先将重试相关的所有可变参数抽离至配置文件实现参数统一管理。打开sqlite_spider/config/column_config.py在文件末尾追加重试配置项python运行# 爬虫自动重试配置 # 单请求最大重试次数 MAX_RETRY_TIMES 3 # 重试间隔时间单位秒 RETRY_DELAY 1.5 # 允许重试的HTTP状态码白名单 RETRY_STATUS_CODES {500, 502, 503, 504, 408, 429} # 框架内置需要重试的异常类型Twisted网络异常 RETRY_EXCEPTIONS ( twisted.internet.error.TimeoutError, twisted.internet.error.ConnectionRefusedError, twisted.internet.error.ConnectionDone, twisted.internet.error.TCPTimedOutError )同时在config/__init__.py中导出新增配置保证全项目可正常引用python运行from .column_config import ( SPIDER_COLUMN_CONFIG, NEXT_PAGE_SELECTOR, SQLITE_DB_PATH, MAX_RETRY_TIMES, RETRY_DELAY, RETRY_STATUS_CODES, RETRY_EXCEPTIONS ) from .settings_dev import * from .settings_prod import * # 环境标识切换此处即可切换整套配置 ENV dev依赖补充中间件需要引入 Twisted 异常类Python 环境已随 Scrapy 自动安装无需额外执行安装命令。三、下载器中间件生命周期方法详解在编写代码前明确下载器中间件三个核心方法的触发时机、入参、返回值与使用规则这是开发自定义中间件的基础。3.1 process_request 方法触发时机请求发送至下载器之前执行。入参request请求对象、spider当前爬虫实例返回值三种规则返回None请求正常向下传递进入下一层中间件或下载器返回Response对象直接截断请求链路将伪造响应回传给爬虫返回Request对象丢弃当前请求重新发起新请求。本次用途为请求对象附加自定义重试计数记录当前请求重试次数。3.2 process_response 方法触发时机下载器获取响应之后回传给爬虫之前执行。入参request、response、spider返回值规则必须返回Request或Response对象。本次用途判断响应状态码是否在重试白名单内若命中则生成重试请求。3.3 process_exception 方法触发时机请求发送过程中抛出异常时执行网络异常、超时、连接失败等。入参request、exception、spider返回值规则返回Request则触发重试返回None则终止请求。本次用途捕获网络异常判断异常类型是否允许重试执行重试逻辑。四、完整代码实现自动重试中间件开发打开sqlite_spider/middlewares/downloader_mw.py编写完整的重试中间件代码包含重试计数、状态码重试、异常重试、延迟控制、日志输出全功能代码附带逐行原理注释。4.1 重试中间件完整代码python运行import time from twisted.internet.error import TimeoutError, ConnectionRefusedError, ConnectionDone, TCPTimedOutError from scrapy.http import Request from sqlite_spider.config import ( MAX_RETRY_TIMES, RETRY_DELAY, RETRY_STATUS_CODES, RETRY_EXCEPTIONS ) class RequestRetryMiddleware: 下载器中间件请求失败自动重试中间件 功能网络异常、服务端状态码异常自动重试限制最大重试次数与重试间隔 def process_request(self, request, spider): 请求预处理初始化重试计数器 每一个新请求进入链路时绑定重试次数标记 # 仅首次请求初始化重试计数避免重复赋值 if not hasattr(request, retry_times): request.retry_times 0 return None def process_response(self, request, response, spider): 响应拦截处理HTTP状态码异常重试 针对5xx、429、408等临时错误执行重试 # 获取当前请求已重试次数 current_retry request.retry_times # 判断响应状态码是否在重试白名单中 if response.status in RETRY_STATUS_CODES: # 超出最大重试次数直接返回原始响应终止重试 if current_retry MAX_RETRY_TIMES: spider.logger.error( f请求状态码异常已达最大重试次数{MAX_RETRY_TIMES}次放弃重试 | fURL{request.url} 状态码{response.status} ) return response # 重试次数自增 request.retry_times 1 # 执行重试延迟降低请求频率 time.sleep(RETRY_DELAY) spider.logger.warning( f状态码异常触发重试当前重试次数{request.retry_times} | fURL{request.url} 状态码{response.status} ) # 生成新请求复用原有请求参数重新加入请求队列 retry_request request.copy() return retry_request # 正常状态码直接返回响应进入爬虫解析逻辑 return response def process_exception(self, request, exception, spider): 异常拦截处理网络层面异常重试 针对超时、连接失败、连接断开等网络异常执行重试 current_retry request.retry_times # 判断当前异常是否属于可重试异常 if isinstance(exception, RETRY_EXCEPTIONS): # 超出最大重试次数终止重试返回None丢弃请求 if current_retry MAX_RETRY_TIMES: spider.logger.error( f网络异常已达最大重试次数{MAX_RETRY_TIMES}次放弃重试 | fURL{request.url} 异常类型{type(exception).__name__} ) return None # 重试次数自增 request.retry_times 1 time.sleep(RETRY_DELAY) spider.logger.warning( f网络异常触发重试当前重试次数{request.retry_times} | fURL{request.url} 异常信息{str(exception)} ) # 复制原请求对象发起重试 retry_request request.copy() return retry_request # 非可重试异常直接丢弃请求 spider.logger.error(f不可重试异常 | URL{request.url} 异常{str(exception)}) return None4.2 代码核心原理深度解析重试计数器原理利用request对象动态绑定retry_times属性记录重试次数该属性跟随请求在中间件链路中传递每一次重试完成计数自增。使用hasattr判断属性是否存在保证仅在请求首次进入时初始化避免计数被重复清零。请求复制原理Scrapy 中request.copy()方法会完整复制原请求的 URL、请求头、Cookie、回调函数、元数据等所有属性保证重试请求与原始请求完全一致不会丢失业务参数。返回新的Request对象后框架会自动将其重新放入调度队列实现二次请求。重试延迟原理通过time.sleep()实现固定间隔等待作用是平滑请求频率防止短时间内多次请求对目标服务器造成压力同时提升网络恢复概率。延迟时间由全局配置统一控制可根据站点规则灵活调整。异常分类处理原理通过isinstance判断异常类型仅对白名单内的临时网络异常执行重试对于 404、403 等永久性错误、未知异常直接终止请求并打印错误日志避免无效循环重试。日志分层原理使用warning级别记录正常重试行为使用error级别记录重试失败、不可重试异常日志层级清晰线上运维可快速区分问题类型。五、中间件启用与全局配置中间件编写完成后必须在settings.py中手动启用同时配置执行优先级。打开项目顶层sqlite_spider/settings.py找到DOWNLOADER_MIDDLEWARES配置项添加重试中间件python运行# 导入环境配置 from sqlite_spider.config import * # 启用数据管道 ITEM_PIPELINES { sqlite_spider.pipelines.SqlitePipeline: 300, } # 下载器中间件配置键中间件完整路径值优先级数字越小执行顺序越靠前 DOWNLOADER_MIDDLEWARES { # 自动重试中间件优先级设置为100 sqlite_spider.middlewares.downloader_mw.RequestRetryMiddleware: 100, } # 爬虫中间件占位后续扩展使用 SPIDER_MIDDLEWARES {} # 自定义扩展占位 EXTENSIONS {} # 全局请求超时时间配合重试机制使用单位秒 DOWNLOAD_TIMEOUT 10配置补充说明优先级规则下载器中间件按照数字从小到大依次执行本次设置100属于高优先级保证重试逻辑优先拦截请求与响应DOWNLOAD_TIMEOUT全局请求超时时间当请求超过 10 秒未返回数据触发TimeoutError异常进入重试逻辑参数可根据业务调整原生 Scrapy 内置重试中间件默认关闭本次为自定义实现两者互不冲突。六、功能测试与场景验证6.1 常规启动测试进入项目根目录执行爬虫启动命令bash运行scrapy crawl book_spider正常网络环境下所有请求均可一次成功终端仅输出正常采集日志无重试相关警告日志证明中间件不影响正常业务流程。6.2 人为模拟网络异常测试模拟断网运行爬虫过程中断开网络连接终端会持续输出网络异常触发重试警告日志重试次数逐步累加当重试次数达到配置的3次上限后输出已达最大重试次数放弃重试错误日志请求终止。恢复网络后新请求正常采集。模拟服务端异常修改目标 URL 为临时返回 503 状态码的测试地址观察状态码重试逻辑验证计数、延迟、上限限制全部生效。6.3 数据完整性验证完成异常场景测试后打开data.db数据库查看数据。经过重试恢复的请求可正常解析并入库仅完全失败的请求缺失数据整体数据采集完整率大幅提升。七、功能优化与进阶扩展方案基于基础重试中间件结合生产环境需求提供多套优化方案可根据业务复杂度选择性迭代。7.1 优化一指数退避重试动态延迟固定延迟在网络恢复缓慢的场景下效果有限指数退避会让重试间隔逐次变长降低后期请求频率。修改延迟逻辑代码python运行# 原固定延迟time.sleep(RETRY_DELAY) # 指数退避第1次延迟1.5s第2次3s第3次6s delay RETRY_DELAY * (2 ** (current_retry - 1)) time.sleep(delay)该方案适合网络波动较大、恢复较慢的站点。7.2 优化二请求去重防循环重试部分页面跳转异常会导致请求无限循环结合 Scrapy 请求指纹机制在重试时增加 URL 去重判断避免同一 URL 反复重试。7.3 优化三区分栏目单独配置重试规则若不同栏目网络环境、反爬规则不同可在column_config.py中为每个栏目单独配置重试次数、延迟、状态码白名单中间件根据请求 URL 匹配对应栏目规则实现精细化控制。7.4 优化四重试数据统计与入库扩展中间件逻辑将重试次数、失败 URL、异常类型、最终结果写入 SQLite 统计表实现可视化统计便于长期运维监控。7.5 优化五结合 UA / 代理中间件联动在重试请求时同步切换 User-Agent 或代理 IP解决因 IP 封禁、UA 限制导致的请求失败实现重试伪装组合容错进一步提升通过率。八、中间件开发规范与线上运维注意事项8.1 中间件开发规范功能单一原则一个中间件文件内按功能划分类重试、UA、代理等功能拆分不同类禁止多类逻辑混合在同一个中间件配置解耦原则所有阈值、白名单、时间参数统一写入config目录禁止硬编码日志规范区分warning/error/info日志级别日志内容包含 URL、次数、异常类型三大核心字段异常捕获中间件内部增加全局try-except防止中间件自身报错导致整个爬虫崩溃。8.2 线上运维注意事项合理设置重试上限最大重试次数建议控制在 2~5 次次数过高会消耗系统资源区分可重试场景严格区分临时异常与永久异常不对 403、404、401 执行重试配合请求延迟使用全局DOWNLOAD_DELAY与重试延迟叠加使用严格遵守目标站点访问规则日志监控线上部署后重点监控重试错误日志高频重试 URL 代表站点访问异常及时排查 IP 封禁、反爬策略变更问题资源管控高并发爬虫场景下重试请求会增加调度队列压力需同步控制并发数。九、本章总结本文基于模块化架构完成了 Scrapy 下载器中间件的开发落地一套完整的请求失败自动重试机制。从中间件分类、生命周期方法、配置设计到代码编写、启用配置、场景测试完整覆盖了生产级重试功能的全流程开发。该重试中间件解决了网络波动、服务端临时故障、请求限流等常见问题显著提升爬虫的稳定性与数据采集完整度同时严格遵循项目模块化、配置解耦、日志标准化的工程化规范可无缝集成至现有项目并长期迭代。目前本系列已完成轻量化入库、多栏目适配、项目模块化、请求自动重试四大核心功能整套架构已具备线上基础运行能力。下一篇将在此架构之上讲解 Scrapy 项目打包、脚本封装、一键启动与全环境部署方案实现爬虫项目快速分发、批量部署。