如何在 Linux 上将 Smartproxy 住宅代理和数据中心代理与 Scrapy 配合使用
构建一个 Scrapy 下载器中间件,在 Linux 上轮换 Smartproxy 住宅和数据中心 HTTP 代理,并使用基于环境的凭据和池回退重试。
概述
Scrapy 内置的 HttpProxyMiddleware 可以通过一个 HTTP 代理路由请求,但使用 Smartproxy 进行生产级抓取通常需要两样东西:一个用于地理定位或抗封禁请求的住宅代理池,以及一个用于高吞吐量请求的数据中心代理池。本教程展示如何添加一个自定义下载器中间件,为每个请求选择一个 Smartproxy 端点,在住宅和数据中心代理池之间轮换,并重试常见的代理错误,而无需在你的 Spider 中硬编码凭据。
Smartproxy 支持 HTTP 和 SOCKS5;本指南使用 HTTP/HTTPS 代理端点,因为 Scrapy 内置的代理中间件期望 HTTP 代理 URL。
先决条件
- 一台装有 Python 3.10 或更高版本的 Linux 机器或容器。
- 一个拥有住宅和/或数据中心代理凭据的 Smartproxy 账户。
- 来自 Smartproxy 仪表板的端点详细信息:你计划使用的每个代理池的代理主机、端口、用户名和密码。
pip和venv可用。
步骤
步骤 1 — 创建虚拟环境并安装 Scrapy
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install scrapy python-dotenv
步骤 2 — 将 Smartproxy 凭据存储为环境变量
不要将凭据提交到你的代码仓库。
export SMARTPROXY_RES_HOST='your-residential-host'
export SMARTPROXY_RES_PORT='your-residential-port'
export SMARTPROXY_RES_USERNAME='your-residential-username'
export SMARTPROXY_RES_PASSWORD='your-residential-password'
export SMARTPROXY_DC_HOST='your-datacenter-host'
export SMARTPROXY_DC_PORT='your-datacenter-port'
export SMARTPROXY_DC_USERNAME='your-datacenter-username'
export SMARTPROXY_DC_PASSWORD='your-datacenter-password'
如果你更喜欢 .env 文件,请在 Scrapy 启动前加载它。并将该文件排除在版本控制之外。
步骤 3 — 创建 Scrapy 项目
scrapy startproject smartproxy_scrapy
cd smartproxy_scrapy
scrapy genspider example example.com
步骤 4 — 添加 Smartproxy 轮换中间件
创建 smartproxy_scrapy/middlewares.py,或将其追加到生成的文件中。该中间件会在 Scrapy 的 HttpProxyMiddleware 运行之前设置 request.meta['proxy']。
import os
from urllib.parse import quote
from scrapy import signals
from scrapy.exceptions import NotConfigured
from scrapy.utils.project import get_project_settings
class SmartproxyRotationMiddleware:
'''Route Scrapy requests through Smartproxy residential or datacenter endpoints.'''
def __init__(self, settings):
self.pools = {
'residential': {
'host': settings.get('SMARTPROXY_RES_HOST'),
'port': settings.get('SMARTPROXY_RES_PORT'),
'username': settings.get('SMARTPROXY_RES_USERNAME'),
'password': settings.get('SMARTPROXY_RES_PASSWORD'),
},
'datacenter': {
'host': settings.get('SMARTPROXY_DC_HOST'),
'port': settings.get('SMARTPROXY_DC_PORT'),
'username': settings.get('SMARTPROXY_DC_USERNAME'),
'password': settings.get('SMARTPROXY_DC_PASSWORD'),
},
}
if not any(pool['host'] and pool['port'] for pool in self.pools.values()):
raise NotConfigured('Set at least one Smartproxy pool host and port.')
@classmethod
def from_crawler(cls, crawler):
middleware = cls(crawler.settings)
crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened)
return middleware
def spider_opened(self, spider):
spider.logger.info('Smartproxy rotation middleware enabled')
def process_request(self, request, spider):
pool_name = request.meta.get('proxy_pool', 'residential')
pool = self.pools.get(pool_name)
if not pool or not pool['host'] or not pool['port']:
return None
host = pool['host']
port = pool['port']
username = quote(pool['username'] or '', safe='')
password = quote(pool['password'] or '', safe='')
if username and password:
proxy_url = f'http://{username}:{password}@{host}:{port}'
else:
proxy_url = f'http://{host}:{port}'
request.meta['proxy'] = proxy_url
request.meta['smartproxy_pool'] = pool_name
return None
步骤 5 — 在 Scrapy 的代理中间件之前启用该中间件
编辑 smartproxy_scrapy/settings.py。
DOWNLOADER_MIDDLEWARES = {
'smartproxy_scrapy.middlewares.SmartproxyRotationMiddleware': 749,
}
HTTPPROXY_ENABLED = True
DOWNLOAD_TIMEOUT = 30
RETRY_HTTP_CODES = [403, 407, 429, 500, 502, 503, 504]
RETRY_TIMES = 2
CONCURRENT_REQUESTS = 16
CONCURRENT_REQUESTS_PER_DOMAIN = 4
Scrapy 内置的 HttpProxyMiddleware 排序为 750,因此 749 可确保你的中间件先设置代理 URL。
步骤 6 — 按请求选择住宅或数据中心代理
对于地理定位或高匿名性页面,使用 meta={'proxy_pool': 'residential'}。对于高吞吐量、稳定的页面,使用 meta={'proxy_pool': 'datacenter'}。
import scrapy
class SmartproxyDemoSpider(scrapy.Spider):
name = 'smartproxy_demo'
start_urls = ['https://httpbin.org/ip']
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={'proxy_pool': 'residential'},
callback=self.parse,
dont_filter=True,
)
yield scrapy.Request(
url,
meta={'proxy_pool': 'datacenter'},
callback=self.parse,
dont_filter=True,
)
def parse(self, response):
yield {
'pool': response.meta.get('smartproxy_pool'),
'status': response.status,
'origin': response.text.strip(),
}
步骤 7 — 在代理池之间添加回退重试
目标网站返回 403 或 429 通常意味着当前 IP 被封禁。在 SmartproxyRotationMiddleware 类中添加此方法,以便失败的请求使用另一个 Smartproxy 代理池重试一次。
def process_response(self, request, response, spider):
retryable = {403, 429}
already_switched = request.meta.get('smartproxy_pool_retry', False)
if response.status in retryable and not already_switched:
current = request.meta.get('proxy_pool', 'residential')
alternate = 'datacenter' if current == 'residential' else 'residential'
new_meta = request.meta.copy()
new_meta['proxy_pool'] = alternate
new_meta['smartproxy_pool_retry'] = True
spider.logger.warning(
'Retrying %s with %s pool after HTTP %s',
request.url,
alternate,
response.status,
)
return request.replace(meta=new_meta, dont_filter=True)
return response
如果你使用此回退机制,请将 RETRY_TIMES 保持较低,以免被阻止的请求无限循环。
步骤 8 — 运行 Spider 并保存结果
scrapy crawl smartproxy_demo -O smartproxy_results.json
步骤 9 — 使用 cURL 验证代理出口 IP
在 Scrapy 之外使用相同的 Smartproxy HTTP 端点,以确认凭据和连通性。
curl --proxy 'http://USERNAME:PASSWORD@HOST:PORT' https://httpbin.org/ip
将 USERNAME、PASSWORD、HOST 和 PORT 替换为你正在测试的代理池的值。
故障排除
407 Proxy Authentication Required:检查用户名和密码,确保它们已进行 URL 编码,并确认你为所选代理池使用了正确的主机和端口。Tunnel connection failed或 TLS 错误:确认 Smartproxy 端点支持通过 HTTP CONNECT 访问 HTTPS 目标。请先使用步骤 9 中的 cURL 命令进行测试。- Scrapy 忽略了代理:确认
SmartproxyRotationMiddleware在DOWNLOADER_MIDDLEWARES中的值低于 750,并且HTTPPROXY_ENABLED为True。 - 频繁出现
403或429:降低CONCURRENT_REQUESTS_PER_DOMAIN,添加DOWNLOAD_DELAY,切换到住宅代理,并且仅在目标允许的情况下轮换 User-Agent。 - 响应缓慢:数据中心代理通常更适合原始吞吐量;住宅代理更适合地理定位请求以及会屏蔽数据中心 IP 段的网站。
- 凭据包含
@、:或/:请保留中间件中的urllib.parse.quote调用。引用可防止代理 URL 格式错误。
总结
你现在拥有一个 Scrapy 项目,可在 Linux 上通过 Smartproxy 住宅和数据中心 HTTP 代理路由请求。该中间件会为每个请求选择一个代理池,将凭据保存在环境变量中,并可在目标屏蔽某个 IP 时回退到另一个代理池。请从低并发开始,使用 cURL 验证出口 IP,并且仅在爬虫行为稳定后才提高 CONCURRENT_REQUESTS_PER_DOMAIN。