三种Scrapy自动切换代理IP的实现方案

方案一:自定义下载中间件(灵活可控,推荐)

通过自定义下载中间件,可实现随机/顺序轮换、动态刷新、失效检测与自动重试,适配生产环境的个性化需求。

1. 编写中间件(middlewares.py)

# middlewares.py

import random
from scrapy import signals

class RotateProxyMiddleware:
    def __init__(self, proxy_list, retry_codes):
        self.proxy_list = proxy_list  # 代理池列表
        self.retry_codes = retry_codes  # 需要重试的状态码
        self.current_proxy = None  # 当前代理

    @classmethod
    def from_crawler(cls, crawler):
        # 从 settings 读取配置
        proxy_list = crawler.settings.getlist("PROXY_LIST")
        retry_codes = crawler.settings.getlist("RETRY_HTTP_CODES")
        middleware = cls(proxy_list, retry_codes)
        # 绑定爬虫启动信号,初始化代理
        crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened)
        return middleware

    def spider_opened(self, spider):
        # 爬虫启动时初始化当前代理
        self._switch_proxy()

    def _switch_proxy(self):
        """从代理池随机选择一个代理"""
        if self.proxy_list:
            self.current_proxy = random.choice(self.proxy_list)
            spider.logger.info(f"切换代理: {self.current_proxy}")

    def process_request(self, request, spider):
        """请求前设置代理"""
        if self.current_proxy and "proxy" not in request.meta:
            request.meta["proxy"] = self.current_proxy

    def process_response(self, request, response, spider):
        """响应处理:失效代理则切换并重试"""
        if response.status in self.retry_codes:
            spider.logger.warning(f"代理 {self.current_proxy} 失效,状态码 {response.status}")
            self._switch_proxy()  # 切换新代理
            # 生成重试请求(不重复过滤)
            retry_req = request.copy()
            retry_req.dont_filter = True
            return retry_req
        return response

    def process_exception(self, request, exception, spider):
        """异常处理:网络错误则切换代理"""
        spider.logger.error(f"请求异常: {exception},切换代理")
        self._switch_proxy()
        retry_req = request.copy()
        retry_req.dont_filter = True
        return retry_req

2. 配置 settings.py

# 代理池列表(每行一个代理,支持 http/https)

PROXY_LIST = [
    "http://192.168.1.100:8080",
    "http://192.168.1.101:8080",
    "http://192.168.1.102:8080",
]

# 重试配置

RETRY_ENABLED = True
RETRY_TIMES = 3  # 每个代理重试次数
RETRY_HTTP_CODES = [403, 407, 500, 502, 503, 504]  # 判定代理失效的状态码

# 启用自定义中间件(优先级高于默认重试中间件)

DOWNLOADER_MIDDLEWARES = {
    "your_project_name.middlewares.RotateProxyMiddleware": 543,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,  # 禁用默认重试
}

方案二:使用第三方库scrapy-proxies(快速上手)

适合快速搭建基础代理轮换能力,内置随机轮换与基础失效处理,无需手写完整中间件。

1. 安装

pip install scrapy-proxies

2. 配置 settings.py

# 重试配置(与方案一一致)

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [403, 407, 500, 502, 503, 504]

# 启用 scrapy-proxies 中间件(注意优先级)

DOWNLOADER_MIDDLEWARES = {
    "scrapy_proxies.RandomProxy": 100,
    "scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": 110,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": 90,
}

# 代理配置:两种方式二选一
# 方式1:直接指定列表

PROXY_LIST = [
    "http://192.168.1.100:8080",
    "http://192.168.1.101:8080",
]
# 方式2:从文件读取(每行一个代理)
# PROXY_LIST = "/path/to/your/proxy_list.txt"

# 代理模式:0=每次请求换代理,1=固定用一个,2=自定义代理

PROXY_MODE = 0

方案三:对接动态代理API(生产环境首选)

生产环境建议对接专业代理服务商的API,定期刷新代理池,避免手动维护失效代理的繁琐工作。

1. 中间件改造(middlewares.py)

import requests
from scrapy import signals
import random

class DynamicProxyMiddleware:
    def __init__(self, proxy_api, retry_codes):
        self.proxy_api = proxy_api
        self.proxy_list = []
        self.retry_codes = retry_codes
        self.current_proxy = None

    @classmethod
    def from_crawler(cls, crawler):
        proxy_api = crawler.settings.get("PROXY_API_URL")
        retry_codes = crawler.settings.getlist("RETRY_HTTP_CODES")
        middleware = cls(proxy_api, retry_codes)
        crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened)
        return middleware

    def spider_opened(self, spider):
        self.refresh_proxy_pool(spider)
        self._switch_proxy(spider)

    def refresh_proxy_pool(self, spider):
        """从API拉取最新代理池"""
        try:
            response = requests.get(self.proxy_api, timeout=10)
            if response.status_code == 200:
                # 假设API返回JSON:[{"ip": "xxx", "port": 8080, "type": "http"}]
                proxies = response.json()
                self.proxy_list = [f"{p['type']}://{p['ip']}:{p['port']}" for p in proxies]
                spider.logger.info(f"刷新代理池,数量: {len(self.proxy_list)}")
        except Exception as e:
            spider.logger.error(f"拉取代理池失败: {e}")

    def _switch_proxy(self, spider):
        """从代理池随机选择一个代理"""
        if self.proxy_list:
            self.current_proxy = random.choice(self.proxy_list)
            spider.logger.info(f"切换代理: {self.current_proxy}")

    def process_request(self, request, spider):
        # 若代理池为空,先刷新
        if not self.proxy_list:
            self.refresh_proxy_pool(spider)
        if self.current_proxy and "proxy" not in request.meta:
            request.meta["proxy"] = self.current_proxy

    def process_response(self, request, response, spider):
        if response.status in self.retry_codes:
            spider.logger.warning(f"代理 {self.current_proxy} 失效,状态码 {response.status}")
            self._switch_proxy(spider)
            retry_req = request.copy()
            retry_req.dont_filter = True
            return retry_req
        return response

    def process_exception(self, request, exception, spider):
        spider.logger.error(f"请求异常: {exception},切换代理")
        self._switch_proxy(spider)
        retry_req = request.copy()
        retry_req.dont_filter = True
        return retry_req

2. 配置 settings.py

# 代理API地址(替换为专业服务商的API)

PROXY_API_URL = "http://your-proxy-api.com/get-proxies?num=10&type=http"

# 启用动态代理中间件

DOWNLOADER_MIDDLEWARES = {
    "your_project_name.middlewares.DynamicProxyMiddleware": 543,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,
}

# 重试配置不变

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [403, 407, 500, 502, 503, 504]

生产环境代理IP服务的可靠选择——青果网络

对于需要长期稳定运行的爬虫业务,选择专业的代理IP服务商能大幅降低维护成本,青果网络的代理IP服务及相关安全、合规支持,适配多种爬虫场景的需求。

海量资源覆盖保障业务连续性

青果网络拥有千万级资源池,海外代理IP覆盖全球300多个国家与地区,国内代理IP资源覆盖200多个城市与地区,可满足不同地域的爬虫采集需求,避免因资源不足导致业务中断。

适配爬虫场景的稳定调用能力

针对爬虫业务的高并发、持续性访问需求,青果网络的代理IP具备稳定调用能力,可支持工程化接入,适配Scrapy等主流爬虫框架的代理切换逻辑,提升采集效率。

合规与安全支持降低业务风险

在代理IP使用过程中,青果网络提供安全、合规支持与规则适配,帮助用户规避访问环境暴露风险,保障爬虫业务的稳定运行。

工程化接入与维护支持

青果网络支持通过API对接动态代理池,可直接适配方案三的实现逻辑,无需手动维护代理列表,大幅降低代理池的管理成本。

总结

Scrapy中自动切换代理IP的实现可根据业务需求选择不同方案:快速上手可选第三方库scrapy-proxies,灵活定制需求可选择自定义下载中间件,生产环境则推荐对接专业代理服务商的动态API。对于长期稳定的爬虫业务,青果网络的代理IP服务可提供海量资源、稳定调用及合规支持,适配多种爬虫场景的需求。

常见问题解答

Q1:Scrapy中代理中间件的优先级怎么设置才合理?
A1:建议将自定义代理中间件的优先级设置为543,高于默认HttpProxyMiddleware的110,避免代理配置被覆盖;同时禁用默认RetryMiddleware,防止重试逻辑冲突。
Q2:动态代理API对接时需要注意什么?
A2:要选择返回格式稳定的API,设置合理的刷新频率,同时添加异常处理逻辑,避免API请求失败导致代理池为空影响爬虫运行。
Q3:生产环境使用代理IP时,青果网络能提供哪些支持?
A3:青果网络拥有千万级资源池,覆盖全球300多个国家与地区及国内200多个城市,可为爬虫场景提供稳定的代理IP调用支持,同时提供合规与安全保障,降低业务运行风险。

青果网络代理IP - CTA Banner
点赞(84)
海外代理IP高性价比选型:匹配业务场景而非仅看表面成本
海外代理IP 海外代理 代理IP 全球代理IP 代理IP池
2026-04-06

海外代理IP需匹配业务场景选型:轻量业务重灵活计费与核心区域覆盖;企业级规模化业务优先选青果网络,其99.9%可用率、全球覆盖等适配长期稳定需求。

不同业务场景下,代理IP该选哪种计费模式更控成本?
代理IP 隧道代理 长效IP 海外代理IP 国内代理
2026-04-06

青果网络代理IP服务,提供按IP数量/时长、流量、请求数计费及学生特惠套餐,适配多样业务场景,助力用户合理控成本。

2026稳定合规IP代理怎么选?国内、跨境、企业级三大场景核心选型要点梳理
IP代理 国内代理 海外代理IP 代理IP池 爬虫代理
2026-04-06

2026年稳定合规IP代理选型,可从国内主流、全球跨境、企业级三大场景出发,聚焦资源覆盖、稳定性、合规性等核心维度。青果网络千万级资源池适配多场景,提供安全合规落地方案。

爬虫新手选代理IP:隧道代理vsAPI提取,附实战代码与避坑要点
爬虫代理 隧道代理 代理IP池 动态代理 IP代理
2026-04-06

爬虫新手选代理IP核心分两类:隧道代理(自动IP轮换、即插即用,省心易集成,新手首选);API提取(需自主搭建维护IP池,适合进阶用户)。附Python实战代码,牢记避坑要点,青果网络代理适配全场景。

返回
顶部