在 Scrapy 中实现自定义代理轮换,标准做法是编写一个下载中间件,在 process_request 方法中为请求绑定 proxy 元数据。该方案适合需要高频请求或应对反爬策略的场景,风险在于无效代理会导致请求失败,需配合异常处理机制。
先说结论:通过自定义 Downloader Middleware 修改 request.meta['proxy'] 是实现轮换的核心路径。
- 适合:需要动态切换出口地址、应对频率限制或 IP 封禁的爬虫任务。
- 先准备:确保持有可用的代理地址列表,并验证连通性。
- 验收:通过日志确认请求携带了正确的代理地址,且目标网站获取到的 IP 已变更。
命令速用版
以下为核心配置代码片段,可直接嵌入项目中间件文件与 settings 配置中。
# middlewares.py
class ProxyRotationMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
return cls(proxy_list)
def process_request(self, request, spider):
if self.proxy_list:
request.meta['proxy'] = random.choice(self.proxy_list)
# settings.py
DOWNLOADER_MIDDLEWARES = {
'your_project.middlewares.ProxyRotationMiddleware': 543,
}
PROXY_LIST = ['http://proxy1:port', 'http://proxy2:port']为什么会这样
Scrapy 的下载器中间件机制允许在请求发送前拦截并修改请求参数。
Scrapy 引擎在发送请求给下载器之前,会依次经过下载器中间件。通过在中间件的 process_request 方法中修改 request.meta 字典,可以动态指定该次请求使用的代理服务器。这种设计解耦了代理逻辑与爬虫逻辑,便于集中管理和轮换。
分步处理
按以下步骤完成中间件编写与启用,每步完成后需检查配置是否生效。
步骤 1:创建中间件类
在项目的 middlewares.py 文件中定义新类。初始化时从配置读取代理列表,确保列表不为空。
步骤 2:实现轮换逻辑
在 process_request 方法中,使用 random 模块从列表中随机选择一个代理地址,赋值给 request.meta['proxy']。若需要更复杂的轮换策略(如权重、失败剔除),可在此处扩展逻辑。
步骤 3:启用中间件
在 settings.py 的 DOWNLOADER_MIDDLEWARES 字典中注册该类,优先级建议设置在 500 到 750 之间,确保在默认重试中间件之前或之后按需调整。
步骤 4:配置代理列表
在 settings.py 中定义 PROXY_LIST 变量,填入完整的代理地址字符串,包含协议头(如 http://)。
怎么验证是否生效
通过日志输出与目标站点反馈双重确认代理是否生效。
检查日志:在中间件中加入 logging.info 打印当前选中的代理地址,运行爬虫时观察控制台输出,确认每次请求或按预期频率变化的代理地址。
检查目标站:请求一个能返回客户端 IP 的测试接口(如 httpbin.org/ip),对比返回的 IP 地址是否与配置的代理地址一致。若返回 IP 与本地出口 IP 相同,则代理未生效。
常见坑
- 认证格式错误:若代理需要用户名密码,需写成 http://user:pass@host:port 格式,直接填入 meta 可能导致认证失败。
- 协议不匹配:目标 URL 是 HTTPS 时,部分代理服务器不支持 CONNECT 隧道,需确认代理支持 HTTPS 请求。
- 列表为空:若配置读取失败导致代理列表为空,中间件应跳过设置,避免报错或发送无代理请求。
常见问题
代理需要认证怎么处理?
将用户名和密码直接拼接到代理地址字符串中,格式为 http://username:password@host:port。
如何在代理失败时自动切换?
在 process_exception 方法中捕获异常,从列表中移除当前失效代理,并设置 request.meta['proxy'] 为新地址,同时返回 request 重新调度。
支持 HTTP 和 HTTPS 混合代理吗?
支持,但建议在代理地址中明确标注协议头,Scrapy 会根据请求 URL 的协议尝试匹配,部分代理服务器可能限制协议类型。
参考来源
- Scrapy 官方文档,Downloader Middleware,https://docs.scrapy.org/