Python爬虫框架Scrapy中代理IP失效后如何自动更换IP

求助各位 v 友,我使用 scrapy 框架爬取数据使用了代理 ip,想问下如何在当前 ip 失效或者被封了之后自动更换 ip 呢
Python爬虫框架Scrapy中代理IP失效后如何自动更换IP

4 回复

需要记录代理 ip 的有效性,如果是使用了 API,先缓存到数据库中。

顺便打个广告,Crawlab 是一个专注于爬虫的集成了爬虫管理、任务调度、任务监控、数据分析等模块的分布式爬虫管理平台,非常适合对爬虫管理、爬虫工程化有要求的开发者及企业

https: //github.com/tikazyq/crawlab


在Scrapy里处理代理IP失效自动切换,核心是用中间件配合重试机制。下面给个完整方案:

# middlewares.py
import random
from scrapy import signals
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

class ProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.current_proxy = None
    
    @classmethod
    def from_crawler(cls, crawler):
        # 从配置文件读取代理列表
        proxy_list = crawler.settings.get('PROXY_LIST', [])
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        if not request.meta.get('proxy') and self.proxy_list:
            proxy = random.choice(self.proxy_list)
            request.meta['proxy'] = proxy
            self.current_proxy = proxy

class RetryWithProxyMiddleware(RetryMiddleware):
    def __init__(self, settings):
        super().__init__(settings)
        self.proxy_list = settings.get('PROXY_LIST', [])
    
    def process_response(self, request, response, spider):
        # 检查是否需要重试
        if response.status in [407, 429, 500, 502, 503, 504]:
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return response
    
    def _retry(self, request, reason, spider):
        # 移除当前失效的代理
        current_proxy = request.meta.get('proxy')
        if current_proxy in self.proxy_list:
            self.proxy_list.remove(current_proxy)
        
        # 选择新代理
        if self.proxy_list:
            new_proxy = random.choice(self.proxy_list)
            request.meta['proxy'] = new_proxy
            spider.logger.info(f'切换代理: {current_proxy} -> {new_proxy}')
        
        return super()._retry(request, reason, spider)
# settings.py
DOWNLOADER_MIDDLEWARES = {
    'your_project.middlewares.ProxyMiddleware': 543,
    'your_project.middlewares.RetryWithProxyMiddleware': 550,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,  # 禁用默认重试中间件
}

RETRY_TIMES = 3  # 重试次数
PROXY_LIST = [
    'http://proxy1:port',
    'http://proxy2:port',
    # ... 更多代理
]
# spiders/example.py
import scrapy

class ExampleSpider(scrapy.Spider):
    name = 'example'
    
    def start_requests(self):
        # 请求会自动使用代理中间件
        yield scrapy.Request('http://example.com', callback=self.parse)
    
    def parse(self, response):
        # 你的解析逻辑
        pass

这个方案的工作原理:

  1. ProxyMiddleware负责给请求分配初始代理
  2. RetryWithProxyMiddleware继承自Scrapy的重试中间件,当遇到代理错误或服务器错误时自动触发重试
  3. 重试时会从代理池中移除失效代理并选择新代理
  4. 通过RETRY_TIMES控制重试次数

关键点:

  • 代理失效检测基于HTTP状态码(407代理认证、429限速、5xx服务器错误)
  • 每次重试都会更换代理IP
  • 代理池用完时会继续使用最后可用的代理

建议搭配代理池服务使用效果更好。

有中间件的,在 middleware 里写,至少按照 response.status 判断是不是 200

建个代理 IP 池,失效或被 ban 就从池子里剔除掉

回到顶部