You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scrapy在外部切换IP时循环重试直至失败问题求助

问题分析与解决方案

嘿,我来帮你搞定这个Scrapy在IP切换时触发的SSL版本错误问题。从你贴的日志来看,核心报错是OpenSSL.SSL.Error: [('SSL routines', 'ssl3_get_record', 'wrong version number')],这个问题本质是IP切换后,Scrapy的下载器还在复用之前绑定旧IP的连接池,而新IP对应的代理/网络环境的SSL配置和旧连接不兼容,导致SSL握手失败,进而触发无限重试循环。

下面是针对性的解决办法,按优先级排序:

1. 禁用连接复用,强制每次请求新建连接

Scrapy默认开启HTTP持久连接(keep-alive),IP切换后旧连接依然绑定原IP,继续复用就会出现SSL不匹配。你可以在settings.py里添加这些配置:

# 指定兼容现代网站的TLS版本
DOWNLOADER_CLIENT_TLS_METHOD = 'TLSv1.2'
# 增加请求延迟,给IP切换留足生效时间
DOWNLOAD_DELAY = 1.5
# 每个域名同时只发一个请求,避免连接池复用冲突
CONCURRENT_REQUESTS_PER_DOMAIN = 1

还可以自定义下载器中间件,彻底清除旧连接:

from scrapy.downloadermiddlewares.httpcompression import HttpCompressionMiddleware

class ResetConnectionMiddleware(HttpCompressionMiddleware):
    def process_request(self, request, spider):
        # 强制关闭之前的连接池,避免复用旧IP的连接
        if hasattr(self, 'pool'):
            self.pool.close()
            self.pool = None
        # 重置请求超时和重定向设置
        request.meta['download_timeout'] = 12
        return None

然后在settings.py里注册这个中间件:

DOWNLOADER_MIDDLEWARES = {
    'your_project.middlewares.ResetConnectionMiddleware': 543,
}

2. 自定义重试中间件,针对SSL错误强制换IP

当遇到特定SSL版本错误时,不要只是无脑重试,而是主动触发IP切换并重置请求的代理信息。扩展Scrapy的RetryMiddleware:

from scrapy.downloadermiddlewares.retry import RetryMiddleware
import OpenSSL

class CustomRetryMiddleware(RetryMiddleware):
    def process_exception(self, request, exception, spider):
        # 检测是否是目标SSL版本错误
        if isinstance(exception, OpenSSL.SSL.Error) and 'wrong version number' in str(exception):
            # 调用你的IP切换程序切换IP
            spider.ip_switcher.switch_ip()
            # 更新请求的代理为新IP对应的代理
            new_proxy = spider.ip_switcher.get_current_proxy()
            request.meta['proxy'] = new_proxy
            # 标记请求不被过滤,强制重新发起
            request.dont_filter = True
            return self._retry(request, "SSL版本不匹配,已切换IP", spider)
        return super().process_exception(request, exception, spider)

接着在settings.py里替换默认的重试中间件:

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
    'your_project.middlewares.CustomRetryMiddleware': 550,
}

3. 调整IP切换时机,避开请求处理中

尽量不要在Scrapy正在建立连接、传输数据的时候切换IP。可以把IP切换逻辑放在请求完成后或者批量请求结束后:
比如用Scrapy的信号机制,每处理10个请求就切换一次IP:

from scrapy import signals
import scrapy

class EbaySpider(scrapy.Spider):
    name = 'ebay_spider'
    
    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = super().from_crawler(crawler, *args, **kwargs)
        crawler.signals.connect(spider.switch_ip_after_batch, signal=signals.request_finished)
        return spider
    
    def switch_ip_after_batch(self, request, response, spider):
        # 每处理10个请求切换一次IP
        if self.crawler.stats.get_value('item_scraped_count', 0) % 10 == 0:
            self.ip_switcher.switch_ip()

4. 提前验证新代理的SSL可用性

IP切换后,先手动测试新代理是否能正常访问目标网站,再让Scrapy使用它:

import requests

def validate_proxy(proxy):
    try:
        # 测试代理访问目标网站的SSL连接
        resp = requests.get(
            'http://www.ebay.com',
            proxies={'https': proxy},
            verify=False,
            timeout=10
        )
        return resp.status_code == 200
    except Exception as e:
        self.logger.warning(f"新代理 {proxy} 验证失败: {e}")
        return False

在你的IP切换程序里,只有当validate_proxy返回True时,才把这个代理传给Scrapy。

内容的提问来源于stack exchange,提问作者Cai Allin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:18:54