HTTP 代理服务器充当您的网页抓取脚本与目标网站之间的中介。Scrapy 爬虫不再直接连接目标,而是先连接到代理服务器,再由代理把请求转发给目标。这样您就可以隐藏自己的 IP 地址、绕过地域限制,并避免被采用反抓取措施的网站封禁。本文提供一份使用 Scrapy 中间件配置和轮换代理的实用指南。
使用中间件在 Scrapy 中配置代理
Scrapy 的中间件系统提供了处理请求和响应的灵活方式。我们可以利用这套系统来实现代理支持。整个过程就是创建一个自定义中间件,拦截请求并为其分配代理服务器。
创建自定义代理中间件
首先,在您的 Scrapy 项目中新建一个 Python 文件(例如 proxy_middleware.py)。该文件将包含自定义代理中间件的代码。
import random
class ProxyMiddleware:
def __init__(self, proxies):
self.proxies = proxies
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings.getlist('PROXIES'))
def process_request(self, request, spider):
proxy = random.choice(self.proxies)
request.meta['proxy'] = proxy
print(f"Using proxy: {proxy}")
def process_response(self, request, response, spider):
# 可选:处理响应状态码,以便换用其他代理重试
if response.status in [403, 429]:
print(f"Proxy {request.meta['proxy']} blocked, retrying with another proxy.")
return self._retry_request(request, spider)
return response
def _retry_request(self, request, spider):
proxy = random.choice(self.proxies)
request.meta['proxy'] = proxy
new_request = request.copy()
return new_request
说明:
__init__(self, proxies): 构造函数接收一个代理列表作为输入。from_crawler(cls, crawler): Scrapy 使用这个类方法创建中间件实例。它从 Scrapy 设置中获取代理列表。process_request(self, request, spider): 该方法在 Scrapy 发送请求之前被调用。它从列表中随机选择一个代理,并赋值给请求的meta['proxy']属性,从而告诉 Scrapy 本次请求使用指定的代理。process_response(self, request, response, spider): 该方法让您可以处理服务器返回的响应。这里会检查 403(Forbidden)或 429(Too Many Requests)等状态码,它们通常表示代理已被封禁。若发现封禁状态码,就换一个代理重试该请求。_retry_request(self, request, spider): 该方法创建一个分配了不同代理的新请求。
配置 Scrapy 设置
接下来,您需要配置 Scrapy 设置以启用中间件并提供代理列表。打开 settings.py 文件并添加以下内容:
# settings.py
# 启用 ProxyMiddleware
DOWNLOADER_MIDDLEWARES = {
'your_project_name.proxy_middleware.ProxyMiddleware': 350, # 按需调整优先级
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None, # 禁用默认的 HttpProxyMiddleware
}
# 代理列表
PROXIES = [
'http://user1:[email protected]:8080',
'http://user2:[email protected]:8080',
'http://user3:[email protected]:8080',
'https://user4:[email protected]:8080',
]
# 代理经常失败,因此多重试几次
RETRY_TIMES = 10
# 代理失败率很高,因此对大多数错误码都重试
RETRY_HTTP_CODES = [500, 502, 503, 504, 400, 408]
# 禁用默认的 user agent 中间件,改用自定义中间件
DOWNLOADER_MIDDLEWARES.update({
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400,
})
# 遵守 robots.txt 规则
ROBOTSTXT_OBEY = False
说明:
DOWNLOADER_MIDDLEWARES: 这个字典用于启用和配置下载器中间件。键是中间件类的路径,值是中间件的优先级。数字越小优先级越高(中间件越早执行)。为避免与自定义中间件冲突,默认的HttpProxyMiddleware被禁用。PROXIES: 该列表包含您想使用的代理服务器。格式为protocol://user:password@host:port。HTTP 和 HTTPS 代理都可以使用。 如果不需要用户名和密码,格式就简化为protocol://host:port。RETRY_TIMES与RETRY_HTTP_CODES: 这些设置用于配置 Scrapy 的重试中间件。 由于代理可能不稳定,建议增加重试次数,并把可能表示代理问题的常见 HTTP 错误码包含进来。DOWNLOADER_MIDDLEWARES.update(...): 这一段禁用默认的 User Agent 中间件,并启用scrapy_user_agents来轮换 User Agent,有助于避免您的抓取器被轻易识别。您需要用pip install scrapy-user-agents安装scrapy_user_agents。
运行爬虫
现在您可以像往常一样运行 Scrapy 爬虫。中间件会自动为每个请求分配一个代理。
scrapy crawl your_spider_name
代理轮换策略
轮换代理对于防止抓取器被封禁至关重要。以下是几种常见策略:
- 随机选择: 如上面的示例所示,为每个请求从列表中随机选择一个代理。这是最简单的方法,但未必最有效。
- 顺序轮换: 按顺序循环使用代理列表。如果您希望每个代理被使用的次数相同,这种方式很有用。
- 智能轮换: 实现追踪各个代理表现的逻辑,并优先使用状态良好的代理。这可能涉及监控响应时间、错误率和其他指标。
- 使用代理 API: 使用能自动处理代理轮换与管理的代理服务 API。这类服务通常提供地理定位和 IP 地址信誉管理等功能。
顺序代理轮换
下面是在中间件中实现顺序代理轮换的示例:
import itertools
class SequentialProxyMiddleware:
def __init__(self, proxies):
self.proxies = itertools.cycle(proxies) # 使用 cycle 轮换代理
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings.getlist('PROXIES'))
def process_request(self, request, spider):
proxy = next(self.proxies) # 从循环中取出下一个代理
request.meta['proxy'] = proxy
print(f"Using proxy: {proxy}")
def process_response(self, request, response, spider):
if response.status in [403, 429]:
print(f"Proxy {request.meta['proxy']} blocked, rotating to the next proxy.")
return self._retry_request(request, spider)
return response
def _retry_request(self, request, spider):
proxy = next(self.proxies)
request.meta['proxy'] = proxy
new_request = request.copy()
return new_request
关键变化:
itertools.cycle(proxies): 它会创建一个在代理列表上无限循环的迭代器。 使用next()函数取出序列中的下一个代理。
请记得把 DOWNLOADER_MIDDLEWARES 设置更新为指向 SequentialProxyMiddleware。
代理 API 集成
与代理 API 集成通常意味着向 API 发送请求以获取代理,并处理 API 的认证与错误响应。 具体做法取决于您选择的 API。 许多代理提供商提供 Python SDK 来简化这一过程。
代理类型
不同代理类型的对比如下:
| 特性 | HTTP 代理 | HTTPS 代理 | SOCKS 代理 |
|---|---|---|---|
| 协议 | HTTP | HTTPS | SOCKS(多个版本) |
| 加密 | 客户端与代理之间不加密 | 客户端与代理之间加密 | 加密取决于 SOCKS 版本 |
| 适用场景 | 网页浏览、抓取 HTTP 站点 | 网页浏览、抓取 HTTPS 站点 | 通用,支持多种协议 |
| 匿名性 | 匿名性可能较低 | 匿名性可能较高 | 可以达到很高的匿名性 |
| 配置方式 | 通常在浏览器中配置 | 通常在浏览器中配置 | 需要 SOCKS 客户端或库支持 |
| 示例 URL | http://host:port |
https://host:port |
socks5://host:port 或 socks4://host:port |
| 认证 | 基本认证(用户名/密码) | 基本认证(用户名/密码) | 支持用户名/密码认证 |
常见问题与排查
- 代理不可用: 确认代理服务器在线且可访问。检查代理的认证凭据(用户名和密码)。确保
settings.py中的代理格式正确。 - 代理被封: 实施代理轮换,并考虑使用拥有大量 IP 地址池的代理服务。监控响应码(403、429),并自动换用其他代理重试请求。
- 性能缓慢: 选择地理位置靠近目标服务器的代理。 测试不同的代理提供商,找到性能可靠的一家。
- HTTPS 错误: 确保您的代理支持 HTTPS 连接。 有些 HTTP 代理只支持 HTTP 流量。
- DNS 泄漏: 使用 SOCKS 代理,或把系统配置为使用代理的 DNS 服务器,以防止 DNS 泄漏。
结论
在 Scrapy 中配置和轮换代理,是构建健壮可靠的网页抓取器的关键。通过使用自定义中间件、实施有效的轮换策略并理解不同的代理类型,您可以显著降低被封禁的风险,并提升抓取项目的性能。请持续监控代理,并根据需要调整策略,以保持最佳的抓取效率。
请记得定期测试代理并监控其性能,以确保抓取器持续有效运行。如需更高级的功能和更省心的管理,可以考虑使用代理管理服务。
