Scrapy代理池系统需求:暂停使用超时/慢速代理
解决Scrapy代理池的黑名单慢速代理需求
嘿,我完全懂你的处境——Scrapoxy在代理轮换和浏览器模拟方面确实能满足你的前两个需求,但它确实缺少内置的慢速/超时代理自动拉黑功能。我之前正好需要实现过一模一样的功能,所以下面就告诉你怎么基于Scrapoxy扩展出这个能力:
一、用Scrapy下载中间件实现黑名单机制
Scrapy的下载中间件是处理请求生命周期的绝佳入口,我们可以在这里监听请求的响应时间、捕获超时异常,从而维护一个动态的代理黑名单。
1. 定义黑名单存储
首先我们需要一个地方存黑名单,简单场景用全局集合就行,分布式爬取的话建议用Redis持久化(避免多个爬虫实例各自维护孤立的黑名单):
# 全局黑名单,存储被拉黑的代理地址 PROXY_BLACKLIST = set() # 代理拉黑的过期时间(比如10分钟后自动解除拉黑,重新尝试) BLACKLIST_EXPIRY = 600 # 单位:秒 # 记录每个代理被拉黑的时间,用于判断是否过期 PROXY_BLACKLIST_TIMESTAMP = {}
2. 编写自定义下载中间件
这个中间件会做三件事:
- 请求发送前,跳过仍在黑名单有效期内的代理
- 捕获超时/连接错误时,将对应代理加入黑名单
- 根据响应延迟判断,把慢速代理加入黑名单
示例代码:
from scrapy.downloadermiddlewares.retry import RetryMiddleware from scrapy.utils.response import response_status_message import time class ProxyBlacklistMiddleware(RetryMiddleware): def __init__(self, settings): super().__init__(settings) self.blacklist = set() self.blacklist_expiry = settings.getint('PROXY_BLACKLIST_EXPIRY', 600) self.blacklist_timestamps = {} @classmethod def from_crawler(cls, crawler): return cls(crawler.settings) def process_request(self, request, spider): # 检查当前请求的代理是否在黑名单中 proxy = request.meta.get('proxy') if proxy in self.blacklist: # 判断是否过了拉黑有效期 if time.time() - self.blacklist_timestamps[proxy] > self.blacklist_expiry: self.blacklist.remove(proxy) del self.blacklist_timestamps[proxy] else: # 触发重试,让Scrapoxy切换到下一个代理 spider.logger.info(f"Proxy {proxy} is still blacklisted, retrying with new proxy") return self._retry(request, "Proxy in blacklist", spider) def process_exception(self, request, exception, spider): # 捕获超时、连接失败等异常,拉黑对应代理 proxy = request.meta.get('proxy') if proxy and isinstance(exception, (TimeoutError, ConnectionError)): spider.logger.warning(f"Proxy {proxy} timed out/connection failed, adding to blacklist") self.blacklist.add(proxy) self.blacklist_timestamps[proxy] = time.time() # 触发重试更换代理 return self._retry(request, str(exception), spider) return super().process_exception(request, exception, spider) def process_response(self, request, response, spider): # 根据响应延迟判断是否拉黑代理(比如延迟超过3秒) latency = request.meta.get('download_latency', 0) if latency > 3: proxy = request.meta.get('proxy') if proxy: spider.logger.warning(f"Proxy {proxy} is slow (latency: {latency:.2f}s), adding to blacklist") self.blacklist.add(proxy) self.blacklist_timestamps[proxy] = time.time() # 如果响应状态码异常,触发重试 if response.status in self.retry_http_codes: reason = response_status_message(response.status) return self._retry(request, reason, spider) return response
3. 在Scrapy配置中启用中间件
打开项目的settings.py,添加以下配置:
# 启用自定义代理黑名单中间件,替换默认的RetryMiddleware DOWNLOADER_MIDDLEWARES = { 'your_spider_project.middlewares.ProxyBlacklistMiddleware': 543, 'scrapy.downloadermiddlewares.retry.RetryMiddleware': None, } # 自定义配置项 PROXY_BLACKLIST_EXPIRY = 600 # 代理拉黑10分钟后自动解除 RETRY_TIMES = 3 # 每个请求最多重试3次 RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429] # 需要重试的状态码
二、和Scrapoxy的代理轮换逻辑结合
Scrapoxy本身会自动轮换代理,我们的中间件触发重试后,Scrapoxy会自动分配下一个可用代理,完美衔接。如果需要更精细的控制,也可以通过Scrapoxy的API拉取代理列表,然后过滤掉黑名单中的代理再供Scrapy使用。
三、额外优化建议
- 分布式场景用Redis存黑名单:如果是多个爬虫实例同时运行,用Redis的集合和哈希表来存储黑名单和时间戳,确保所有实例共享同一套黑名单规则。
- 动态调整延迟阈值:可以根据爬虫的运行负载动态调整慢速代理的判断阈值,比如高峰期把阈值从3秒放宽到5秒。
- 定时健康检查:写一个小脚本定时检查黑名单中的代理,提前恢复那些已经恢复速度的代理,避免浪费资源。
内容的提问来源于stack exchange,提问作者Ryflex
相关产品推荐
相关产品推荐

