Вежливый скрейпинг, который масштабируется: руководство по ограничению скорости
Если вы будете слишком активно обращаться к сайту, вас заблокируют; если будете отправлять по одному запросу за раз, никуда не продвинетесь. Масштабируемый компромисс — это адаптивное распределение нагрузки по доменам, распределённое по чистым IP-адресам — вежливо по отношению к сайту, быстро для вас.
Почти половина всего интернет-трафика теперь автоматизирована, и сайты это знают. Ограничение скорости — их первая и самая простая защита — это ограничение скорости, которое определяет, сколько вы можете взять и как быстро. Искушение заключается в том, чтобы рассматривать это как препятствие, которое нужно преодолеть с помощью грубой силы, но это приведёт к блокировке. Противоположная ошибка — отправлять один аккуратный запрос за раз, что не даст результатов в масштабе. Масштабируемое решение — вежливый скрейпинг: подстраивайте скорость под каждую цель, как это сделал бы тяжёлый, но легитимный пользователь, распределяйте нагрузку по чистым IP-адресам и позволяйте ответам сайта регулировать вашу скорость. Так вы останетесь быстрыми, не становясь трафиком, который блокирует всех.
Знайте, какое ограничение вы превышаете
Серверы учитывают ваши запросы по идентификатору — обычно это ваш IP, иногда ключ API или аккаунт — в течение временного окна и действуют, когда вы превышаете порог. Три распространённых модели ведут себя по-разному: фиксированное окно считает запросы за календарную минуту; ведро токенов выдаёт вам фиксированное количество токенов (например, 100 в минуту) и тратит один на каждый запрос, заставляя ждать, когда ведро опустеет; скользящее окно считает за последние 60 секунд в любой момент. Практическое следствие заключается в том, что всплеск более опасен, чем стабильный поток — десять запросов за одну секунду могут превысить лимит, который сто, распределённых за минуту, не превысят.
Ограничения также бывают разных видов. Мягкие ограничения мягкие: сервер замедляет вас или возвращает 429 Too Many Requests с заголовком Retry-After, указывающим, сколько времени ждать (ошибка 1015 от Cloudflare — это оно). Некоторые сайты терпят небольшие всплески — позволяя 100 в минуту, но ограничивая только при 120 — в то время как другие ограничивают рано, скажем, при 15 в минуту, и только жёстко блокируют при 30. Жёсткие ограничения — это строгие потолки: API, позволяющий 1,000 запросов в час, полностью блокирует вас, как только вы его превысите. Если вы повторно превышаете мягкое ограничение, оно усиливается: 429 становится 403, затем временная блокировка на минуты или часы, окно которой увеличивается каждый раз, когда вы его превышаете, и, наконец, постоянный чёрный список вашего IP или всей подсети.
Читайте ответ, не догадывайтесь
Самое большое улучшение для скрейпера — это реагировать на то, что говорит сервер, а не отправлять запросы с фиксированной скоростью. Изучите словарь: 429 означает замедлиться и соблюдать Retry-After; 403 означает, что эта идентификация отмечена, поэтому меняйте, а не повторяйте; 503 часто является вызовом или временным отказом; а 200, возвращающий страницу CAPTCHA, — это мягкая блокировка, а не успех. Обрабатывайте каждый случай по-разному. Неправильное действие — повторное использование сгоревшего IP на 403 или игнорирование Retry-After и продолжение на 429 — это то, что превращает мягкое предупреждение в постоянную блокировку. Наши глубокие погружения в исправление 429 подробно описывают обработку ответов.
import time, requests
def polite_get(session, url, max_tries=4):
for attempt in range(max_tries):
r = session.get(url, timeout=20)
if r.status_code == 200 and "captcha" not in r.text.lower():
return r
if r.status_code == 429: # obey the server
wait = int(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
continue
if r.status_code in (403, 503): # this exit is burned
rotate_ip(session) # fresh IP, then retry
time.sleep(2 ** attempt) # exponential backoff
continue
return r
return None

Распределяйте запросы по доменам
Скрейпер, который обращается ко многим сайтам, никогда не должен применять один глобальный темп ко всем из них. Маленький блог и защищённый рынок терпят совершенно разные нагрузки, поэтому дайте каждому домену свой бюджет. Ведро токенов на хост — это чистый шаблон: назначьте консервативную скорость на домен, пополняйте его со временем и позволяйте запросам к разным хостам выполняться параллельно, в то время как запросы к одному и тому же хосту остаются в пределах его лимита. Начинайте медленно на новой цели и позвольте кодам ответов сказать вам, можете ли вы ускориться.
import time
from collections import defaultdict
class DomainLimiter:
def __init__(self, per_min=30):
self.gap = 60.0 / per_min # min seconds between hits per host
self.last = defaultdict(float)
def wait(self, host):
now = time.time()
delay = self.gap - (now - self.last[host])
if delay > 0:
time.sleep(delay)
self.last[host] = time.time()
# 30 req/min to any single host; different hosts proceed independently
limiter = DomainLimiter(per_min=30)
limiter.wait("example.com")
Распределяйте нагрузку так, чтобы каждый IP оставался вежливым
Вот шаг, который примиряет "вежливость" с "масштабируемостью": вежливость измеряется на IP, но ваша общая пропускная способность — это сумма по IP. Если цель терпит 30 запросов в минуту на адрес, один IP ограничивает вас до 30 — но десять чистых IP, каждый из которых делает 30, дают вам 300 в минуту, в то время как каждый индивидуальный выход остаётся вежливым. Ротация резидентных шлюзов делает это автоматически, выдавая свежий IP на каждый запрос через более чем 90 миллионов адресов, так что ни один выход не выглядит агрессивным. Это не трюк, чтобы бить сильнее; это распределение настоящей нагрузки, чтобы ни один сервер не испытывал подозрительного всплеска. Основы ротации IP изложены в что такое ротация IP и почему это важно.
Распределяйте нагрузку по чистым ротационным IP
Берите меньше, кэшируйте больше, выбирайте своё время
Самый вежливый запрос — это тот, который вы никогда не отправляете. Три привычки снижают нагрузку, не лишая вас данных. Во-первых, кэшируйте агрессивно и используйте условные запросы — отправляйте If-Modified-Since или If-None-Match, чтобы неизменённая страница возвращала маленький 304 вместо полного тела, что экономит сервер и вашу полосу пропускания. Во-вторых, распределяйте параллелизм осознанно: семафор, ограничивающий количество активных запросов на домен, предотвращает случайные всплески. В-третьих, планируйте тяжёлые задачи на непиковые часы цели, когда ваш трафик составляет меньшую долю их и менее вероятно, что он превысит порог. В совокупности это может сократить количество запросов, необходимых для выполнения задачи, вдвое — дисциплина, лежащая в основе нашего более широкого контрольного списка по предотвращению блокировки.
Ещё одна вещь, которую стоит сказать прямо: проверьте robots.txt и соблюдайте заявленные ожидания сайта по сканированию. Вежливость — это не только самосохранение — быть хорошим гражданином помогает сохранить открытый веб доступным для всех. Наше руководство по robots.txt на практике охватывает, что он делает и не делает обязательным.

Часто задаваемые вопросы
Как избежать ограничения скорости при скрейпинге?
Распределяйте нагрузку по доменам с собственным бюджетом запросов, соблюдайте Retry-After на 429, снижайте нагрузку экспоненциально и распределяйте нагрузку по ротационному пулу чистых IP, чтобы ни один адрес не выглядел агрессивным. Добавьте кэширование и условные запросы, чтобы отправлять меньше запросов в целом, и планируйте тяжёлые задачи на непиковые часы цели.
Что означает HTTP 429 и как с ним справляться?
429 Too Many Requests — это мягкое ограничение скорости — сервер просит вас замедлиться, а не блокирует вас. Прочтите заголовок Retry-After и ждите ровно столько, прежде чем повторить попытку; если его нет, снижайте нагрузку экспоненциально. Никогда не игнорируйте его и не продолжайте, потому что повторные 429 превращаются в 403, а затем в временные или постоянные блокировки.
Сколько запросов в минуту безопасно?
Нет универсального числа — это полностью зависит от цели. Маленький сайт может терпеть только несколько запросов в минуту; большой — гораздо больше. Начинайте консервативно (скажем, 20–30 в минуту на IP), следите за 429 и корректируйте по ответам. Масштабируйте общую пропускную способность, добавляя IP, а не увеличивая скорость на одном.
Считается ли ротация прокси невежливой?
Нет, если она используется для распределения настоящей нагрузки. Ротация удерживает каждый отдельный IP в рамках вежливой скорости, в то время как ваша совокупная пропускная способность растёт — сервер никогда не видит подозрительного всплеска от одного адреса. Это становится невежливо только если вы используете её, чтобы превысить то, что сайт может разумно обработать в целом; распределяйте совокупную нагрузку, а не только скорость на IP.
Вежливость и масштабируемость не противоположности. Читайте сигналы, соблюдайте Retry-After, распределяйте бюджет по доменам, кэшируйте, что можете, и распределяйте остальное по чистым ротационным IP. Вы окажетесь быстрее, чем неосторожный скрейпер — потому что вы тот, кого никогда не блокируют.