
三种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调用支持,同时提供合规与安全保障,降低业务运行风险。