Scrapy 中高级 ProxyScrape 轮换:带重试和封禁检测的自定义下载器中间件
构建一个自定义 Scrapy 下载器中间件,轮换 ProxyScrape 住宅和数据中心代理,处理重试并检测封禁,以实现可靠的大规模抓取。
概述
Scrapy 是一个用于大规模网页抓取的强大 Python 框架,但如果没有代理轮换,你很快就会遇到速率限制和 IP 封禁。本教程将向你展示如何构建一个自定义 Scrapy 下载器中间件,用于轮换 ProxyScrape 住宅和数据中心代理、自动处理重试并检测封禁,从而实现可靠的数据收集。
ProxyScrape 提供住宅、数据中心和移动代理,支持 HTTP 和 SOCKS5,拥有 97M+ IP 池,并具备强大的长期正常运行时间。你可以根据抓取量选择按量付费或按月固定计费。
先决条件
- 已安装 Python 3.8 或更高版本。
- 已安装 Scrapy(
pip install scrapy)。 - 拥有一个启用了住宅或数据中心代理的 ProxyScrape 账户。
- 从 ProxyScrape 仪表板获取你的代理端点和凭据。
- 可选:如果你更喜欢静态轮换而不是轮换端点,可以准备一个单独代理端点列表。
步骤 1:创建 Scrapy 项目
打开终端并创建一个新的 Scrapy 项目:
pip install scrapy
scrapy startproject proxyscrape_scraper
cd proxyscrape_scraper
步骤 2:安全存储 ProxyScrape 凭据
切勿在源文件中硬编码凭据。使用环境变量并在 settings.py 中读取它们。
创建一个 .env 文件或在 shell 中导出变量:
export PROXYSCRAPE_USER='your_username'
export PROXYSCRAPE_PASS='your_password'
export PROXYSCRAPE_ENDPOINT='http://proxy.proxyscrape.com:8080'
然后在 settings.py 中添加:
import os
PROXYSCRAPE_USER = os.getenv('PROXYSCRAPE_USER')
PROXYSCRAPE_PASS = os.getenv('PROXYSCRAPE_PASS')
PROXYSCRAPE_ENDPOINT = os.getenv('PROXYSCRAPE_ENDPOINT')
步骤 3:构建轮换中间件
创建一个新文件 proxyscrape_scraper/middlewares.py,并定义一个中间件,该中间件:
- 从列表中选择一个代理,或使用你的轮换端点。
- 将凭据注入代理 URL。
- 在出现与封禁相关的 HTTP 状态码时进行重试。
# proxyscrape_scraper/middlewares.py
import random
import logging
from scrapy import signals
from scrapy.exceptions import NotConfigured
class ProxyScrapeRotationMiddleware:
def __init__(self, proxy_list, credentials):
self.proxy_list = proxy_list
self.credentials = credentials
self.logger = logging.getLogger(__name__)
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
credentials = (
crawler.settings.get('PROXYSCRAPE_USER'),
crawler.settings.get('PROXYSCRAPE_PASS'),
)
if not proxy_list:
raise NotConfigured('PROXY_LIST is empty')
return cls(proxy_list, credentials)
def process_request(self, request, spider):
proxy = random.choice(self.proxy_list)
user, password = self.credentials
if user and password:
proxy = proxy.replace('://', f'://{user}:{password}@')
request.meta['proxy'] = proxy
self.logger.debug(f'Using proxy: {proxy}')
def process_response(self, request, response, spider):
if response.status in [403, 429, 503]:
self.logger.warning(
f'Ban detected with status {response.status} on {request.url}'
)
request.meta['proxy'] = None
return request.replace(dont_filter=True)
return response
步骤 4:配置轮换和重试设置
在 settings.py 中,定义你的代理列表、启用中间件并调整重试行为。
# settings.py
PROXY_LIST = [
'http://proxy1.proxyscrape.com:8080',
'socks5://proxy2.proxyscrape.com:1080',
# 从你的 ProxyScrape 仪表板添加更多端点
]
DOWNLOADER_MIDDLEWARES = {
'proxyscrape_scraper.middlewares.ProxyScrapeRotationMiddleware': 543,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
}
RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [403, 429, 500, 502, 503, 504]
步骤 5:可选 — 使用会话 ID 的粘性会话
如果你的 ProxyScrape 套餐支持通过用户名参数使用粘性会话,你可以将会话 ID 附加到用户名后。请查看你的 ProxyScrape 仪表板以获取确切语法(通常类似于 username-session-abc123)。
# 在 process_request 内部,替换用户名逻辑:
session_id = ''.join(random.choices('abcdef0123456789', k=8))
user = f'{self.credentials[0]}-session-{session_id}'
proxy = proxy.replace('://', f'://{user}:{self.credentials[1]}@')
当你需要在同一域名的多个请求之间保持相同 IP 时(例如在登录流程或多步骤结账期间),请使用粘性会话。
步骤 6:测试你的轮换爬虫
创建一个简单的爬虫,以验证请求正在通过不同的代理。
# proxyscrape_scraper/spiders/ip_test.py
import scrapy
class IpTestSpider(scrapy.Spider):
name = 'ip_test'
start_urls = ['https://httpbin.org/ip']
def parse(self, response):
yield {'ip': response.json()['origin']}
运行爬虫并保存结果:
scrapy crawl ip_test -o ips.json
打开 ips.json 并确认 ip 值不同。如果你反复看到相同的 IP,请检查你的 PROXY_LIST 或轮换端点配置。
为 Scrapy 选择合适的 ProxyScrape 代理类型
| 代理类型 | 最适合 | 轮换 | 粘性会话 | 协议支持 |
|---|---|---|---|---|
| 住宅 | 通用网页抓取、地理定向数据 | 轮换端点或列表 | 通常可通过会话 ID 获得 | HTTP, SOCKS5 |
| 数据中心 | 非保护网站的高速抓取 | 轮换端点或列表 | 通常为静态 | HTTP, SOCKS5 |
| 移动 | 仅移动端网站、社交媒体、应用测试 | 轮换端点 | 通常可用 | HTTP, SOCKS5 |
故障排除
- 身份验证错误(407 Proxy Authentication Required): 请仔细检查你的用户名和密码。如果使用轮换端点,请确保凭据以
user:pass@host:port形式正确嵌入。 - 连接超时: 尝试从 HTTP 切换到 SOCKS5,或反之。某些网站会更积极地封锁某一种协议。同时请验证你的防火墙允许代理端口上的出站流量。
- 未检测到封禁: 如果你仍然收到 CAPTCHA 或空数据,请扩展封禁检测,以查找响应正文中的 CAPTCHA 指示符(例如,HTML 中出现
captcha)。你还可以降低请求速率并增加DOWNLOAD_DELAY。 - SOCKS5 错误: Scrapy 要求
requests库支持 SOCKS,才能使用 SOCKS5 代理。如果你看到Missing dependencies for SOCKS support,请使用pip install requests[socks]安装。 - 粘性会话不起作用: 请在 ProxyScrape 仪表板中确认确切的会话参数格式。有些套餐使用
username-session-{id},而另一些则使用单独的密码字段。
总结
你现在拥有一个可用于生产的 Scrapy 中间件,它可以轮换 ProxyScrape 代理、在封禁时重试,并可选地维持粘性会话。此设置让你能够扩展抓取项目,而不必担心速率限制或 IP 封锁。为获得最佳效果,请将轮换住宅代理与保守的下载延迟相结合,并监控你的成功率。随着抓取目标的变化,调整你的 PROXY_LIST 和重试逻辑。