Прокси-middleware для Scrapy: полный гайд по настройке (2026)

Опубликовано 4 июня 2026 г. · ≈9 мин чтения

Scrapy в 2026 году всё ещё рабочая лошадка для боевых краулеров — и всё ещё фреймворк, в котором настройка прокси сбивает людей с толку сильнее всего, потому что прокси можно воткнуть в четыре разных места, и три из них для большинства проектов неправильные. Этот гайд даёт правильный вариант: небольшой кастомный middleware с маршрутизацией на уровне запроса, sticky-сессиями, детекцией банов и вменяемой логикой повторов.

Если вы на обычных requests/httpx/aiohttp, смотрите Как ротировать прокси в Python. Эта статья — именно про Scrapy.

Версия на 30 секунд

Для ротируемого резидентного шлюза минимально рабочая настройка — одна строка на запрос, без всякого middleware:

def start_requests(self):
    for url in self.urls:
        yield scrapy.Request(
            url,
            meta={"proxy": "http://USERNAME:[email protected]:913"},
        )

Встроенный в Scrapy HttpProxyMiddleware читает request.meta["proxy"] и берёт аутентификацию из URL. Шлюз сам ротирует исходящий IP за вас. Если больше ничего не нужно — на этом можно остановиться. Остальная часть гайда — для случаев, когда вам нужен контроль: sticky-сессии, маршрутизация по стране, ротация с учётом банов и тюнинг конкурентности.

Боевой прокси-middleware

Положите это в middlewares.py. Он назначает sticky-сессию на каждый домен, ротирует при банах и тегирует каждый запрос, чтобы вы могли отлаживать, какая сессия что забрала:

import random
import string

GATEWAY = "us.jibaoproxy.com:913"
USERNAME = "USERNAME"          # в реальных проектах вынесите в settings.py / env
PASSWORD = "PASSWORD"

def _new_session(n=8):
    return "".join(random.choices(string.ascii_lowercase + string.digits, k=n))

class JibaoProxyMiddleware:
    """Sticky-сессия на домен; ротация сессии при бане."""

    def __init__(self):
        self.sessions = {}          # домен -> id сессии

    def _proxy_url(self, session_id):
        user = f"{USERNAME}-session-{session_id}"
        return f"http://{user}:{PASSWORD}@{GATEWAY}"

    def process_request(self, request, spider):
        domain = request.url.split("/")[2]
        session = self.sessions.setdefault(domain, _new_session())
        request.meta["proxy"] = self._proxy_url(session)
        request.meta["proxy_session"] = session

    def rotate(self, domain):
        """Вызывать, когда сессия сожжена."""
        self.sessions[domain] = _new_session()

И парный downloader-middleware, который детектит баны и повторяет запрос на свежей сессии:

from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

BAN_CODES = {403, 429}
BAN_MARKERS = (b"captcha", b"access denied", b"unusual traffic")

class BanAwareRetryMiddleware(RetryMiddleware):

    def process_response(self, request, response, spider):
        banned = (
            response.status in BAN_CODES
            or any(m in response.body[:2048].lower() for m in BAN_MARKERS)
        )
        if banned:
            domain = request.url.split("/")[2]
            proxy_mw = spider.crawler.engine.downloader.middleware.middlewares
            for mw in proxy_mw:
                if hasattr(mw, "rotate"):
                    mw.rotate(domain)        # сжечь сессию
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return super().process_response(request, response, spider)

Подключите оба в settings.py:

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.JibaoProxyMiddleware": 350,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,   # заменяем штатный retry
    "myproject.middlewares.BanAwareRetryMiddleware": 550,
}
RETRY_TIMES = 2

Приоритет важен: прокси-middleware должен отрабатывать раньше scrapy'шного HttpProxyMiddleware (750), так что любое значение ниже 750 годится; 350 держит его рано и предсказуемо.

Sticky или ротация: какой режим под какой паук

Тип краулингаРежимРеализация
Сбор страниц без состоянияРотацияГолый username, шлюз ротирует на каждый запрос
Логин + краулинг под авторизациейSticky на аккаунт-session-{account_id}, никогда не ротировать в середине логина
Листинги с тяжёлой пагинациейSticky на домен, ротация при банеMiddleware выше
Гео-специфичные ценыРотация + привязка к странеПараметры вида USERNAME-country-de

Подробнее об этом компромиссе: Sticky или ротируемые прокси-сессии.

Настройки конкурентности, с которыми вас не забанят

Дефолты Scrapy заточены под вежливый краулинг с одного IP. За ротируемым пулом можно давить куда сильнее — но лимиты на домен всё равно важны, потому что цель видит совокупное поведение:

# settings.py - вменяемая отправная точка за резидентным пулом
CONCURRENT_REQUESTS = 64
CONCURRENT_REQUESTS_PER_DOMAIN = 8     # то, что ощущает цель
DOWNLOAD_DELAY = 0.25                  # джиттер применяется на каждый слот
RANDOMIZE_DOWNLOAD_DELAY = True        # 0.5x-1.5x от задержки
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 6.0
DOWNLOAD_TIMEOUT = 30
ROBOTSTXT_OBEY = True

Повышайте CONCURRENT_REQUESTS_PER_DOMAIN только после того, как понаблюдаете за долей 403 на текущем уровне на протяжении пары тысяч запросов. Прыжок 8 → 32 по логике «прокси всё равно ротируются» — именно так люди сжигают гигабайты на повторах.

Дисциплина трафика (резидентные гигабайты = деньги)

Частые сбои и что они на самом деле значат

407 Proxy Authentication Required

Учётные данные не дошли до прокси. Положите их в URL (http://user:pass@host:port) внутри meta["proxy"] — Scrapy распарсит и сам выставит Proxy-Authorization. Если выставить заголовок вручную и при этом использовать креды в URL, начинается двойная аутентификация и странности; выберите что-то одно.

TunnelError: Could not open CONNECT tunnel

Почти всегда опечатка в хосте/порте либо HTTPS-цель через эндпоинт, который не разрешает CONNECT на этом порту. Сначала проверьте через curl -x вне Scrapy.

Паук работает 10 минут, потом везде 403

Ваша sticky-сессия пережила свою полезность, либо ваш темп на домен слишком горячий. Первый случай решает ban-aware middleware выше; для второго понизьте CONCURRENT_REQUESTS_PER_DOMAIN. Если цель проверяет JA4, то выдавать вас может сам TLS-стек Scrapy — см. разбор JA3/JA4 о том, почему это не лечится никаким прокси.

Бесплатный инструмент · без регистрации

Проверьте список прокси до краулинга

Вставьте эндпоинты в наш Proxy Checker: он массово проверяет связность, задержку, уровень анонимности и тип исходящего IP — ловите мёртвые или неверно помеченные прокси прежде, чем Scrapy потратит на них повторы.

Проверить мои прокси →

Устали нянчиться с бесплатными списками? Один резидентный шлюз заменяет всё это — получите $5 бесплатного баланса →

Итог

Направьте пауков на настоящий пул

Ротируемые и sticky резидентные сессии на одном шлюзе. $5 бесплатного баланса, чтобы начать краулить.

Начать бесплатно

Все IP-продукты · огромный пул узлов, доступных в любой момент

Зарегистрируйтесь сейчас и получите до 100% кэшбэка на пополнение

Новым пользователям — 5U при регистрации, бонус к первому пополнению. Акция ограничена по времени.