Skip to content
Статья / 2 мин. чтения

Ротация прокси для веб-скрапинга: Python requests, middleware Scrapy и отслеживание позиций

Практическое руководство по ротации прокси в Python и Scrapy — выбор пула, липкие сессии против ротации на каждый запрос, рабочий код middleware, обнаружение блокировок и почему трекерам позиций нужна согласованность локации, а не просто объём IP.

Если вы спарсите несколько сотен страниц с одного IP, рано или поздно вы столкнётесь с ограничением скорости, CAPTCHA или мягкой блокировкой. Ротация — стандартное решение, но плохая ротация хуже, чем её отсутствие: она сжигает бюджет на прокси, усложняет отладку и может выглядеть ещё более по-ботовски, чем стабильный вежливый краулер.

Это руководство рассказывает, как работает ротация, как реализовать её в Python с помощью requests и в Scrapy с помощью middleware загрузчика, и как те же методы применимы к отслеживанию позиций в SERP.

Прежде чем приступать к ротации, проверьте, не в ротации ли проблема

Ротация скрывает блокировки на уровне IP. Она не решает:

  • Проблема шаблона. Никакое количество IP не спасёт краулер, который запрашивает один и тот же URL пятьдесят раз в секунду с user-agent по умолчанию.
  • Проблема сессии. Если ваш сценарий требует оставаться в системе, новый IP посреди сессии выглядит как кража аккаунта.
  • Проблема геолокации. Если нужный вам контент заблокирован по региону, вам нужна правильная страна, а не больше IP.

Сначала проведите диагностику: логируйте коды состояния, тела ответов и время ответа для каждого IP. Если каждый IP получает 200, а одна конкретная страница возвращает блокировку, ваша проблема — в поведении запросов, а не в репутации IP.

Выберите тип прокси, подходящий для задачи

Тип Типичные преимущества На что обратить внимание
Дата-центр Быстрые, высокая параллельность, широко доступны Легко идентифицируются; блокируются строгими сайтами
Резидентные Смешиваются с трафиком реальных пользователей Медленнее; ограничения по пропускной способности; важна работа с сессиями
Мобильные Высокое доверие, сложнее всего блокировать Дорогие; нестабильные; меньше одновременных сессий
ISP / статические резидентные Стабильный, постоянный IP Меньше пулы; меньше ротации

Для структурированных данных на лояльных сайтах обычно достаточно дата-центров. Для поисковой выдачи, маркетплейсов и туристических сайтов, как правило, требуются резидентные или мобильные прокси. Оценивайте провайдеров по гранулярности ротации, контролю сессий, геопокрытию и обработке блокировок — а не по заявленному количеству IP.

Стратегии ротации

Ротация на каждый запрос

Каждый запрос уходит с IP, выбранного из пула. Многие провайдеры реализуют это как один ротируемый шлюз (имя хоста) плюс порт или как токен сессии в имени пользователя. Лучший вариант для запросов страниц без сохранения состояния.

Липкие сессии

Один и тот же IP переиспользуется в течение заданного окна или пока вы его не освободите. Используйте это для многошаговых сценариев: поиск, открытие листинга, чтение страницы с деталями. В большинстве коммерческих пулов липкость управляется идентификатором сессии в имени пользователя прокси, и новый идентификатор даёт новый IP.

Географическая привязка

Для отслеживания позиций и проверки цен обычно требуется один и тот же город или страна для набора запросов, поскольку результаты зависят от местоположения. Ротируйте внутри гео, а не между гео.

Ограничение параллельности

Ротация и ограничение скорости дополняют друг друга, а не заменяют. Краулер с пятью одновременными запросами, соблюдающий Retry-After, покажет лучшие результаты, чем краулер с двумя сотнями потоков, долбящих каждый IP, пока его не забанят.

Python: ротация прокси с помощью requests

Установите поддержку SOCKS, если она нужна. Библиотека requests использует PySocks под капотом:

pip install "requests[socks]"

Затем выполняйте ротацию по пулу:

import itertools
import requests

POOL = [
    'http://user:[email protected]:8000',
    'http://user:[email protected]:8000',
    'socks5h://user:[email protected]:1080',
]

proxy_cycle = itertools.cycle(POOL)

def fetch(url, timeout=20):
    proxy = next(proxy_cycle)
    proxies = {'http': proxy, 'https': proxy}
    response = requests.get(url, proxies=proxies, timeout=timeout)
    response.raise_for_status()
    return response.text

Несколько моментов, которые стоит добавить в продакшене:

  • Повторяйте запрос с другим прокси при 429 или 403, а не с тем же самым.
  • Используйте requests.Session() для переиспользования соединений, но создавайте одну сессию на IP, а не одну сессию на весь краулинг.
  • Логируйте, какой прокси дал какой результат, чтобы вы могли отбрасывать плохие IP, а не снова их выбирать.
  • Используйте socks5h:// (обратите внимание на h), когда хотите, чтобы имена хостов разрешались на прокси, а не локально.

Scrapy: middleware загрузчика с ротацией прокси

Scrapy уже обрабатывает аутентификацию прокси через встроенный middleware HTTP-прокси, поэтому вам нужно только установить request.meta['proxy'] перед отправкой запроса. Простой middleware ротации выглядит так:

import random

class RotatingProxyMiddleware:
    def __init__(self, proxies):
        self.proxies = proxies

    @classmethod
    def from_crawler(cls, crawler):
        proxies = crawler.settings.getlist('ROTATING_PROXIES')
        if not proxies:
            raise ValueError('ROTATING_PROXIES is empty')
        return cls(proxies)

    def process_request(self, request, spider):
        request.meta['proxy'] = random.choice(self.proxies)

Зарегистрируйте его в модуле настроек:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RotatingProxyMiddleware': 350,
}

ROTATING_PROXIES = [
    'http://user:[email protected]:8000',
    'http://user:[email protected]:8000',
]

AUTOTHROTTLE_ENABLED = True
RETRY_HTTP_CODES = [403, 429, 500, 502, 503, 504]

Два замечания:

  • Наивный random.choice будет продолжать использовать забаненные IP. Такие проекты сообщества, как scrapy-rotating-proxies, добавляют обнаружение блокировок и статистику по каждому прокси поверх той же идеи; эта зависимость оправдана, когда вы масштабируетесь дальше нескольких запросов.
  • Middleware повторных попыток Scrapy обычно повторяет запрос с тем же прокси. Если баны — ваша проблема, заставьте путь повторной попытки заново выбирать прокси.

Применение того же подхода к отслеживанию позиций

У трекеров позиций есть требование к прокси, которого нет у обычных скраперов: согласованность локации. Ключевое слово, проверенное из одного города, а затем из другого, даст разные SERP и бессмысленный график движения.

Практические правила для отслеживания позиций:

  • Выберите одну локацию для каждого отслеживаемого ключевого слова и сохраняйте её стабильной между запусками.
  • Периодически обновляйте IP, чтобы вас не пометили, но оставайтесь в пределах того же города или региона.
  • Используйте мобильные прокси при отслеживании мобильной выдачи, потому что десктопные и мобильные SERP различаются достаточно сильно, чтобы это имело значение.
  • Агрессивно кэшируйте и проверяйте по расписанию, а не непрерывно.

Также стоит прямо сказать: скрапинг поисковых систем обычно нарушает их условия обслуживания. Если вам нужны надёжные данные о позициях, официальные API и лицензированные фиды для отслеживания позиций — путь с меньшим риском. Прокси нужны для случаев, когда API действительно не существует.

Как понять, работает ли ротация

Отслеживайте эти показатели за весь краулинг, а не по каждому запросу:

  • Доля успешных запросов на прокси, то есть ответы 2xx, делённые на количество попыток.
  • Доля блокировок на прокси с указанием типа блокировки: 403, CAPTCHA, пустое тело, страница согласия.
  • Медианное время ответа на прокси. Прокси, который работает, но тратит тридцать секунд на страницу, — чистый убыток.
  • Фактически использованные уникальные IP по сравнению с размером настроенного пула.

Если доля успешных запросов одинакова для всех IP, ваша проблема не в репутации IP, и никакая ротация не поможет.

Вывод

Ротируйте только после того, как исправили частоту запросов, заголовки и логику сессий. Используйте ротацию на каждый запрос для запросов без сохранения состояния и липкие сессии для всего многошагового. В Python requests требует дополнения SOCKS и прокси на сессию, тогда как в Scrapy middleware загрузчика, назначающий request.meta['proxy'], делает большую часть работы, а пакеты для отслеживания банов — остальное. А для отслеживания позиций считайте географию требованием, а ротацию — обслуживанием.