You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:30:56