Skip to content
Advanced / 3 min read

Scrapy 中高级 ProxyScrape 轮换:带重试和封禁检测的自定义下载器中间件

构建一个自定义 Scrapy 下载器中间件,轮换 ProxyScrape 住宅和数据中心代理,处理重试并检测封禁,以实现可靠的大规模抓取。

Linux SOCKS5 HTTP(S) 网页抓取

概述

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 和重试逻辑。