Исправление 429 Too Many Requests: откат, бюджеты и распределение IP
429 — это единственный блок, который точно указывает, как его исправить — если вы читаете заголовки ответа, а не просто повторяете попытку. Вот арифметика безопасной пропускной способности скрапинга.
HTTP 429 Too Many Requests — это самый честный блок, который может отправить вам веб-сайт. В отличие от 403, он называет проблему — вы слишком быстро — и часто предоставляет решение в заголовке ответа. Однако стандартная реакция — обернуть вызов в цикл повторных попыток и надеяться, что это превратит решаемую проблему темпа в медленный, блокирующий беспорядок. Это руководство рассматривает 429 как то, чем он является: математической задачей с четырьмя рычагами. Прочтите ответ, соблюдайте опубликованный лимит, правильно откатывайтесь, когда превышаете его, и распределяйте оставшуюся нагрузку по идентичностям.
Что означает 429 и когда он лжет
Ограничитель скорости считает запросы по идентичности — обычно по IP-адресу, иногда по API-ключу или cookie сессии — в пределах временного окна. Перейдите порог, и вы получите 429 вместо контента. Реализации различаются: токен-бакеты выдают фиксированное количество, которое пополняется по расписанию, скользящие окна считают за скользящий период, а не за минуту по часам, а многоуровневые системы ограничивают на мягком лимите перед жесткой блокировкой на более высоком. Какой из них вы встретите, определяет, достаточно ли короткой паузы или нужно ждать целое окно.
Теперь оговорка, которая сэкономит часы: 429 на ваш первый запрос — это не ограничение скорости. Это ответ бота, замаскированный под ограничение скорости. Широко читаемая ветка на Stack Overflow описывает именно это — первый вызов скрапера вернул страницу с надписью "Misbehaving Content Scraper Please use robots.txt Your IP has been rate limited" вместе с 429. Ничего не было превышено; сервер просто решил, что клиент — бот, и выбрал этот код. Если вы видите 429 до того, как отправили какой-либо объем, рассматривайте это как проблему обнаружения и работайте с заголовками и проверками IP в нашем руководстве по 403 forbidden errors.
Прочтите ответ, прежде чем менять код
Добросовестные серверы сообщают, когда вернуться. Retry-After содержит либо количество секунд, либо дату HTTP. Многие API добавляют X-RateLimit-Limit (потолок), X-RateLimit-Remaining (что осталось в текущем окне) и X-RateLimit-Reset (когда оно пополняется). Эти три превращают реактивные повторные попытки в проактивное соблюдение темпа: вы можете замедлиться до блока, а не после него.
import requests
r = requests.get("https://target.example/api/items", timeout=20)
print(r.status_code)
for h in ("Retry-After", "X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset"):
if h in r.headers:
print(f"{h}: {r.headers[h]}")
# No headers at all? The limit is undocumented - measure it:
# send a slow ramp (1 req/s, then 2, then 4) and note where 429 starts.
Если ничего полезного не возвращается, измерьте лимит самостоятельно с помощью рампы: запускайте по одному запросу в секунду в течение минуты, затем два, затем четыре и записывайте скорость, при которой появляются 429. Десять минут измерений лучше недели догадок, и найденное число становится бюджетом, на котором строится все остальное.

Арифметика темпа
Возьмите задокументированный лимит и разделите. Ограничение в 100 запросов в минуту означает 60 / 100 = 0,6 секунды между запросами как абсолютный минимум — и минимум не является целью. Сетевая задержка варьируется, ваши часы и серверные не совпадают, и всплеск на границе окна может удвоить вашу кажущуюся скорость. Стремитесь к 70-80% от лимита: примерно 0,8 секунды на запрос в этом примере, что все еще дает вам 75 страниц в минуту.
Параллелизм следует из того же числа. Если вы хотите 1,25 запроса в секунду и каждый запрос занимает 2 секунды на круг, вам нужно 1,25 x 2 = 2,5 активных запроса — так что семафор 3, а не 50, на которые по умолчанию настроен ваш асинхронный код. Неограниченное асинхронное разветвление — это самая распространенная причина 429: сто корутин, запущенных одновременно, прибывают как один мгновенный всплеск, независимо от того, насколько вежливо выглядит среднее значение. Если вы скрапите с asyncio, шаблоны семафоров в асинхронном Python скрапинге с httpx и aiohttp — это решение.
Откат, который работает: экспоненциальный, ограниченный, с джиттером
Когда вы сталкиваетесь с 429, уважайте Retry-After, если он присутствует. В противном случае начните с одной секунды и удваивайте — 1, 2, 4, 8, 16 — до жесткого ограничения, чтобы сломанная цель не могла навсегда остановить вашу очередь. Затем добавьте джиттер. Без рандомизации каждый рабочий, который столкнулся со стеной в один и тот же момент, повторяет попытку в один и тот же момент, воспроизводя всплеск, вызвавший проблему.
import random, time, requests
def get_with_backoff(session, url, max_tries=6, cap=120.0):
for attempt in range(max_tries):
r = session.get(url, timeout=20)
if r.status_code != 429:
return r
ra = r.headers.get("Retry-After", "")
wait = float(ra) if ra.isdigit() else 2.0 ** attempt # 1, 2, 4, 8, 16, 32
wait = min(wait, cap)
wait += random.uniform(0, wait * 0.3) # jitter: break the lockstep
time.sleep(wait)
raise RuntimeError(f"still 429 after {max_tries} attempts: {url}")
Еще лучше, замкните цикл. Аддитивное увеличение/мультипликативное уменьшение дает вам скрапер, который сам находит лимит и остается чуть ниже его: увеличивайте скорость, пока ответы чисты, уменьшайте ее вдвое, как только прилетает 429. Держите одного темпорегулятора на домен — лимиты зависят от хоста, и одна агрессивная цель не должна замедлять остальные сорок.
class Pacer:
"""One per domain. Additive increase, multiplicative decrease."""
def __init__(self, rps=2.0, floor=0.2, ceiling=8.0):
self.rps, self.floor, self.ceiling = rps, floor, ceiling
def ok(self): # clean response: creep faster
self.rps = min(self.ceiling, self.rps + 0.05)
def throttled(self): # 429: halve immediately
self.rps = max(self.floor, self.rps / 2)
@property
def gap(self):
return 1.0 / self.rps
Распределение нагрузки: бюджет зависит от идентичности, а не от проекта
Когда вы правильно соблюдаете темп и все еще нуждаетесь в большей пропускной способности, единственный оставшийся рычаг — это идентичности. Поскольку счетчик привязан к вашему IP, N выходных IP дают вам N раз больше бюджета — арифметика настолько проста. Если сайт допускает 60 запросов в минуту на адрес, а вам нужно 1 200 страниц в минуту, это 20 одновременных выходов, работающих комфортно ниже лимита, а не один выход, работающий в двадцать раз выше него.
Это то, для чего на самом деле предназначены ротационные прокси. Ротационный шлюз назначает каждому запросу другой резидентный IP из пула более 90 миллионов адресов в более чем 200 странах, так что счетчики по IP никогда не заполняются. Два правила делают разницу между распределением нагрузки и сжиганием пула: держите скорость по IP ниже лимита даже после ротации (ротация умножает ваш бюджет, она его не убирает), и используйте липкие сессии для любого потока, который охватывает несколько запросов — вход в систему, корзина, набор результатов с разбивкой на страницы — чтобы сессия не прерывалась на полпути. Когда вам нужен один и тот же IP на несколько минут и новый после этого, компромиссы изложены в липкие против ротационных сессий.
Умножьте свой бюджет скорости с помощью резидентных IP

Дешевле, чем больше IP: отправляйте меньше запросов
- Дедуплицируйте перед извлечением. Большинство очередей скрапинга содержат один и тот же URL с параметрами отслеживания и завершающими слэшами — сначала канонизируйте.
- Используйте условные GET. Отправьте If-Modified-Since или If-None-Match, и 304 обойдется вам в долю пропускной способности и все равно засчитывается как один запрос.
- Предпочитайте JSON-эндпоинты вместо рендеренного HTML. Один вызов API часто заменяет десять извлечений страниц, и лимиты API обычно документированы.
- Кэшируйте агрессивно во время разработки. Повторный запуск парсера против сохраненных ответов обходится в ноль запросов и ноль блокировок.
- Скрапьте в непиковое время в местном часовом поясе цели. Та же скорость меньше нагружает сервер в 03:00 и реже ограничивается.
- Извлекайте только то, что меняется. Ежедневный полный скрапинг каталога, который обновляется еженедельно, — это шесть потраченных впустую скрапингов.
Дисциплина пропускной способности окупается дважды — меньше запросов означает меньше 429 и меньший счет, что является тем же аргументом, который мы приводим в снижении затрат на пропускную способность прокси. И если вы предпочитаете вообще не строить инфраструктуру темпа, QuantumProxies Scraper API поглощает повторные попытки, ротацию и ограничение по доменам за одним конечным точкой, которая возвращает markdown, JSON или HTML.
Часто задаваемые вопросы
Как избежать HTTP ошибки 429 too many requests в Python?
Установите преднамеренный интервал между запросами на основе опубликованного лимита цели, ограничьте параллелизм семафором, размер которого соответствует скорости, умноженной на задержку, уважайте Retry-After, когда он появляется, и повторяйте попытку с экспоненциальным откатом и джиттером. Если после этого вам нужно больше пропускной способности, распределите запросы по ротационным прокси IP, а не сокращайте интервал.
Как долго ждать после 429?
Ровно столько, сколько указано в Retry-After, если сервер его отправляет — это может быть количество секунд или дата HTTP. Без этого заголовка начните с одной секунды и удваивайте при каждом последующем 429 до ограничения в минуту или две, добавляя случайный джиттер, чтобы параллельные рабочие не повторяли попытку одновременно.
Исправляют ли прокси ошибки 429?
Они умножают ваш бюджет, они не снимают лимит. Поскольку счетчики привязаны к IP клиента, распределение запуска по многим резидентным выходам удерживает каждый адрес ниже порога. Но пул, который подвергается нагрузке в десять раз выше лимита по IP, все равно будет собирать 429 — и сжигать свою репутацию. Сначала соблюдайте темп, затем ротацию.
Является ли 429 тем же, что и бан?
Нет. 429 по замыслу временный и исчезает, когда окно сбрасывается, что отличает его от 403 блокировки идентичности. Игнорирование его неоднократно — это то, как он становится постоянным: устойчивое превышение — это именно тот сигнал, который переводит ограничение в более долгосрочный бан IP.
Почему я получаю 429 на самый первый запрос?
Потому что ничего на самом деле не было подсчитано. Некоторые серверы возвращают 429 любому клиенту, которого они считают ботом, независимо от объема — код статуса просто их выбранный ответ. Проверьте тело ответа: если оно упоминает robots.txt, скраперы или брандмауэр, исправьте свои заголовки, отпечаток TLS и выходной IP, а не ваш темп.
Относитесь к ограничениям скорости как к бюджету, который вы тратите сознательно. Измерьте потолок, работайте на уровне 70-80% от него, откатывайтесь с джиттером, когда превышаете, и покупайте больше идентичностей только после того, как темп будет правильным. Сделано в этом порядке, 429 перестают быть классом ошибок и становятся числом в конфигурационном файле.
Получите ротационные прокси и прекратите бороться с ограничениями скорости