
为什么需要自定义代理中间件
当你用Scrapy爬取网站时,经常会遇到IP被限制的情况。网站发现同一个IP频繁访问,就会封禁这个IP。这时候就需要使用代理IP来更换访问身份,避免被封。
虽然Scrapy有自带的代理中间件,但功能比较基础。比如你需要从ipipgo这样的服务商获取IP,或者需要动态切换不同类型的代理(比如动态住宅代理和静态住宅代理),自带的中间件就难以满足需求了。自定义代理中间件可以让你完全控制代理的使用逻辑。
Scrapy中间件基础概念
在Scrapy中,中间件是处理请求和响应的组件。代理中间件属于下载器中间件,它在发送请求之前工作,主要负责给请求设置代理。
Scrapy的工作流程中,中间件就像一个个过滤器,请求和响应都会经过它们。代理中间件的位置比较靠前,它在请求被发送到网络之前给请求设置代理服务器地址。
搭建基础代理中间件框架
首先创建一个新的Scrapy项目,然后在middlewares.py文件中添加代理中间件类:
import random
from scrapy import signals
class IPIPGoProxyMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
@classmethod
def from_crawler(cls, crawler):
从设置中获取代理列表
proxy_list = crawler.settings.get('PROXY_LIST', [])
return cls(proxy_list)
def process_request(self, request, spider):
如果请求已经设置了代理,就不重复设置
if 'proxy' in request.meta:
return
随机选择一个代理
proxy = random.choice(self.proxy_list)
request.meta['proxy'] = proxy
这个基础框架实现了最简单的代理功能:从代理列表中随机选择一个代理IP。但实际使用中,我们需要更复杂的逻辑。
集成ipipgo代理IP服务
ipipgo提供多种代理IP服务,我们需要根据不同的使用场景选择合适的类型。下面是主要代理类型的对比:
| 代理类型 | 特点 | 适用场景 |
|---|---|---|
| 动态住宅代理 | IP不断更换,匿名性高 | 大规模数据采集 |
| 静态住宅代理 | IP固定不变,稳定性好 | 需要固定IP的业务 |
集成ipipgo服务的完整中间件示例:
import requests
import time
from scrapy.downloadermiddlewares.retry import RetryMiddleware
class IPIPGoAdvancedProxyMiddleware(RetryMiddleware):
def __init__(self, settings):
super().__init__(settings)
self.api_url = "https://api.ipipgo.com/getProxy" ipipgo API地址
self.api_key = settings.get('IPIPGO_API_KEY')
self.proxy_type = settings.get('PROXY_TYPE', 'dynamic') 默认动态代理
self.current_proxy = None
self.proxy_expire_time = 0
def get_new_proxy(self):
"""从ipipgo获取新的代理IP"""
params = {
'apiKey': self.api_key,
'type': self.proxy_type,
'protocol': 'http' 支持HTTP和SOCKS5
}
try:
response = requests.get(self.api_url, params=params, timeout=10)
if response.status_code == 200:
proxy_data = response.json()
self.current_proxy = f"http://{proxy_data['ip']}:{proxy_data['port']}"
设置代理过期时间(根据套餐类型调整)
self.proxy_expire_time = time.time() + 300 5分钟
return True
except Exception as e:
spider.logger.error(f"获取ipipgo代理失败: {e}")
return False
def process_request(self, request, spider):
检查是否需要更换代理
if (not self.current_proxy or
time.time() > self.proxy_expire_time or
request.meta.get('change_proxy', False)):
if not self.get_new_proxy():
spider.logger.warning("获取新代理失败,使用直连")
return
设置代理
request.meta['proxy'] = self.current_proxy
添加代理认证信息(如果需要)
if hasattr(spider, 'proxy_user') and hasattr(spider, 'proxy_pass'):
request.headers['Proxy-Authorization'] = f"Basic {base64.b64encode(f'{spider.proxy_user}:{spider.proxy_pass}'.encode()).decode()}"
高级功能实现
基础的代理切换功能已经可以实现,但实际项目中还需要更多高级功能:
1. 智能代理轮换策略
根据不同的网站特点制定代理使用策略。比如对反爬严格的网站增加代理更换频率:
def should_change_proxy(self, request, spider):
"""判断是否需要更换代理"""
domain = request.url.split('/')[2]
根据域名设置不同的代理策略
strict_domains = ['example.com', 'target-site.com']
if domain in strict_domains:
严格网站每次请求都换代理
return True
普通网站按时间轮换
return time.time() > self.proxy_expire_time
2. 代理IP质量监控
监控代理IP的响应时间和成功率,自动剔除质量差的代理:
class ProxyPerformanceMonitor:
def __init__(self):
self.proxy_stats = {}
def record_proxy_performance(self, proxy, response_time, success):
"""记录代理性能数据"""
if proxy not in self.proxy_stats:
self.proxy_stats[proxy] = {
'total_requests': 0,
'success_requests': 0,
'total_response_time': 0,
'last_used': time.time()
}
stats = self.proxy_stats[proxy]
stats['total_requests'] += 1
stats['total_response_time'] += response_time
if success:
stats['success_requests'] += 1
stats['last_used'] = time.time()
def get_best_proxy(self):
"""获取性能最好的代理"""
if not self.proxy_stats:
return None
根据成功率和响应时间评分
scored_proxies = []
for proxy, stats in self.proxy_stats.items():
success_rate = stats['success_requests'] / stats['total_requests']
avg_response_time = stats['total_response_time'] / stats['total_requests']
score = success_rate 100 - avg_response_time
scored_proxies.append((proxy, score))
返回评分最高的代理
return max(scored_proxies, key=lambda x: x[1])[0]
配置和使用中间件
创建好中间件后,需要在Scrapy项目中正确配置:
1. 修改settings.py文件:
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.IPIPGoAdvancedProxyMiddleware': 543,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None, 禁用默认代理中间件
}
ipipgo配置
IPIPGO_API_KEY = 'your_api_key_here'
PROXY_TYPE = 'dynamic' 使用动态住宅代理
重试设置
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 403]
2. 在爬虫中使用:
class MySpider(scrapy.Spider):
name = 'example'
def start_requests(self):
urls = ['http://example.com/page1', 'http://example.com/page2']
for url in urls:
yield scrapy.Request(url=url, callback=self.parse,
meta={'change_proxy': True}) 强制更换代理
def parse(self, response):
处理响应
if response.status == 403:
遇到封禁,重试请求并更换代理
retryreq = response.request.copy()
retryreq.meta['change_proxy'] = True
yield retryreq
常见问题与解决方案
Q: 代理连接超时怎么办?
A: 可以设置合理的超时时间,并在超时后自动更换代理。在settings.py中设置:
DOWNLOAD_TIMEOUT = 30
Q: 如何避免被网站识别为爬虫?
A: 除了使用代理IP,还应该配合User-Agent轮换、请求频率控制等策略。ipipgo的动态住宅代理IP来自真实家庭网络,更难被识别。
Q: 代理IP验证失败怎么处理?
A: 在中间件中添加代理验证逻辑,定期检查代理是否可用,自动剔除失效的代理。
Q: 如何选择ipipgo的代理类型?
A: 根据业务需求选择:大规模采集用动态住宅代理,需要稳定IP的业务用静态住宅代理。ipipgo的静态住宅代理具有99.9%的可用性,适合长期稳定业务。
Q: 代理中间件影响爬取速度怎么办?
A: 可以预先获取一批代理IP,减少实时获取的频率。同时使用连接池技术复用代理连接。
最佳实践建议
在实际项目中使用代理中间件时,建议注意以下几点:
1. 合理设置代理更换频率
不要过于频繁更换代理,也不要长时间使用同一个代理。根据目标网站的反爬策略调整更换频率。
2. 监控代理性能
建立代理IP质量监控体系,记录每个代理的响应时间、成功率等指标,优先使用高质量的代理。
3. 错误处理机制
完善的错误处理机制很重要,包括代理失效、网络超时、认证失败等各种异常情况的处理。
4. 与ipipgo服务配合使用
充分利用ipipgo提供的各种功能,比如指定国家城市、自定义IP时效等,这些功能可以大大提高爬虫的效率和成功率。
通过本文介绍的方法,你可以构建一个功能完善的Scrapy代理中间件,有效解决IP被封的问题,提高爬虫的稳定性和效率。记得根据实际业务需求调整中间件的具体实现,特别是与ipipgo代理服务的集成方式。

