Scrapy代理中间件的基本结构
在Scrapy框架里,通过中间件(Middleware)来设置代理是最标准、最灵活的方式。你不需要去修改每一个请求的代码,只需要在中间件里统一处理,这样既方便管理,也便于维护。核心思路是编写一个下载器中间件(Downloader Middleware),在请求被发送出去之前,为它设置代理服务器(proxy)的地址。
这个中间件需要放在项目的middlewares.py文件里,并在settings.py中进行启用和排序。一个最基础的代理中间件结构如下:
项目名/middlewares.py
import random
class RandomProxyMiddleware(object):
def __init__(self, proxy_list):
self.proxies = proxy_list
@classmethod
def from_crawler(cls, crawler):
从 settings.py 中加载代理IP列表
proxy_list = crawler.settings.get('PROXY_LIST', [])
return cls(proxy_list)
def process_request(self, request, spider):
随机选择一个代理IP
if self.proxies:
proxy = random.choice(self.proxies)
request.meta['proxy'] = proxy
可选:打印日志,方便调试
spider.logger.debug(f'使用代理: {proxy}')
接下来,你需要在settings.py中进行配置:
settings.py
1. 定义你的代理IP列表,格式为:'协议://IP:端口'
PROXY_LIST = [
'http://12.34.56.78:8080',
'https://98.76.54.32:8888',
... 更多代理IP
]
2. 启用自定义的下载器中间件
DOWNLOADER_MIDDLEWARES = {
'你的项目名.middlewares.RandomProxyMiddleware': 100, 数字越小优先级越高
需要关闭Scrapy默认的代理中间件,否则可能冲突
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None,
}
这样,Scrapy在发送每个请求时,都会经过你的RandomProxyMiddleware,并为其随机分配一个代理IP。
避坑要点:处理代理失效与请求重试
直接使用上面的基础代码,你会遇到第一个大坑:代理IP失效。网络上的免费代理或质量不高的代理,稳定性很差,经常会出现连接超时、拒绝请求或者返回非200状态码的情况。如果不对这些情况做处理,你的爬虫会频繁报错,效率极低。
一个健壮的代理中间件必须包含异常处理和失效代理移除机制。我们需要在process_exception和process_response方法中判断代理是否可用。
class RobustProxyMiddleware(object):
def __init__(self, proxy_list):
self.proxies = proxy_list
可以准备一个“坏代理”列表,暂时屏蔽失效的代理
self.bad_proxies = set()
def process_request(self, request, spider):
如果请求已经指定了代理,则不再分配(尊重已有设置)
if 'proxy' in request.meta or not self.proxies:
return
优先从可用的代理池中选取(排除已知坏代理)
available_proxies = [p for p in self.proxies if p not in self.bad_proxies]
if not available_proxies:
available_proxies = self.proxies 如果没有可用代理,则重置尝试所有
self.bad_proxies.clear()
proxy = random.choice(available_proxies)
request.meta['proxy'] = proxy
记录这个请求使用了哪个代理,方便后续判断
request.meta['_proxy'] = proxy
def process_exception(self, request, exception, spider):
当请求发生异常(如连接超时、代理拒绝连接)时
proxy = request.meta.get('_proxy')
if proxy:
spider.logger.warning(f'代理 {proxy} 抛出异常: {exception}, 将其加入临时黑名单。')
self.bad_proxies.add(proxy)
从request.meta中删除代理,以便重试时使用新代理
if 'proxy' in request.meta:
del request.meta['proxy']
def process_response(self, request, response, spider):
检查响应状态码,如果遇到403、429等状态,可能也是代理问题
proxy = request.meta.get('_proxy')
if response.status in [403, 408, 429, 500, 502, 503, 504]:
if proxy:
spider.logger.warning(f'代理 {proxy} 返回状态码 {response.status}, 将其加入临时黑名单。')
self.bad_proxies.add(proxy)
return response
确保在settings.py中开启了重试中间件,并设置了合理的重试次数:
settings.py
RETRY_ENABLED = True
RETRY_TIMES = 3 重试次数
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429, 403] 需要重试的状态码
这样,当代理失效导致请求失败或返回错误码时,Scrapy会自动重试,而你的中间件会在重试前换掉坏的代理,大大提升了爬虫的健壮性。
高质量代理IP的选择与集成
自己维护一个免费代理IP池非常耗时,且稳定性和匿名性都无法保证。对于商业或稳定的数据采集项目,使用专业的代理IP服务是更明智的选择。以神龙IP代理为例,其提供的API接口可以轻松集成到Scrapy中间件中。
神龙IP代理的动态高级套餐,日更200万+IP,支持灵活控制IP存活时间,非常适合Scrapy这种需要频繁请求IP、高并发请求的场景。其6Mbps的峰值带宽和响应,能有效避免因代理速度慢导致的请求延迟。
集成方式通常是通过调用服务商提供的API,动态获取一个可用的代理IP。下面是一个集成示例:
import requests
class ShenlongProxyMiddleware(object):
def __init__(self, api_url):
self.api_url = api_url 神龙IP代理提供的API获取地址
@classmethod
def from_crawler(cls, crawler):
api_url = crawler.settings.get('SHENLONG_PROXY_API')
return cls(api_url)
def process_request(self, request, spider):
如果请求已经指定了代理,则不再分配
if 'proxy' in request.meta:
return
try:
从神龙IP代理API获取一个代理
resp = requests.get(self.api_url, timeout=5)
if resp.status_code == 200:
proxy_ip_port = resp.text.strip() API通常返回 "ip:port" 格式
根据你的请求协议(http/https)拼接完整的代理地址
request.meta['proxy'] = f'http://{proxy_ip_port}'
spider.logger.debug(f'从神龙API获取代理: {request.meta["proxy"]}')
else:
spider.logger.error(f'获取代理API失败,状态码: {resp.status_code}')
except Exception as e:
spider.logger.error(f'调用神龙代理API异常: {e}')
在settings.py中配置你的API地址:
SHENLONG_PROXY_API = '你的神龙IP代理API地址'
DOWNLOADER_MIDDLEWARES = {
'你的项目名.middlewares.ShenlongProxyMiddleware': 100,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None,
}
使用这种方式的优势在于,IP由服务商保证高匿、纯净和稳定,你无需关心IP的清洗和验证,可以更专注于业务逻辑。神龙IP代理的IP池覆盖广、安全性高,能有效避免因IP被目标网站封禁而导致的数据采集中断。
常见问题与解答(QA)
Q1: 为什么设置了代理,但爬虫还是被网站识别并屏蔽了?
A1: 这可能由几个原因导致:1) 代理IP质量不高,已经是公开透明或被目标网站拉黑的IP。2) 即使使用了高匿代理,但你的请求头(User-Agent、Cookie等)没有进行随机化或模拟浏览器,行为特征依然明显。3) 访问频率过高,触发了网站的防爬策略。解决方案是:使用像神龙IP代理这样的高匿纯净IP服务;同时结合scrapy-fake-useragent等库随机化请求头;并在settings.py中合理设置DOWNLOAD_DELAY和CONCURRENT_REQUESTS来控制爬取速度。
Q2: 如何处理需要认证的代理?
A2: 有些代理服务(包括神龙IP代理的部分协议)需要使用用户名和密码进行认证。在Scrapy中设置认证代理有两种常见方法:
方法一:将认证信息直接嵌入代理URL中。
格式:http://user:pass@ip:port
request.meta['proxy'] = 'http://username:password@12.34.56.78:8080'
方法二:使用requests库风格的认证,但Scrapy原生不支持。更通用的做法是自定义Proxy-Authorization请求头(适用于HTTP Basic Auth)。
import base64
proxy = "12.34.56.78:8080"
auth = "username:password"
encoded_auth = base64.b64encode(auth.encode()).decode()
request.headers['Proxy-Authorization'] = f'Basic {encoded_auth}'
request.meta['proxy'] = f'http://{proxy}'
建议查阅你所使用的代理服务商(如神龙IP代理)提供的具体接入文档,选择其推荐的认证方式。

