Quais são os limites da taxa de raspagem?
Limites de taxa são as paredes invisíveis que os sites constroem para controlar o quão rápido qualquer cliente pode fazer pedidos. Quando você raspa um site muito agressivamente, você atinge essas paredes — e as consequências variam de desacelerações temporárias a proibições de IP permanentes. Entender como os limites de taxa funcionam, como eles o detectam e como ficar sob eles é fundamental para construir raspadores que fornecem dados de forma confiável.
Este guia explica a mecânica por trás da limitação de taxa, o uso de sites de sinais de detecção e estratégias práticas para estrangulamento adaptativo que mantêm seus raspadores funcionando suavemente.
Para uma visão mais ampla da raspagem com proxies, consulte Guia completo de Web Raspando Proxies. Para evitar blocos em geral, leia Como raspar sites sem ser bloqueado.
Como a limitação da taxa funciona
Sites implementam limites de taxa em várias camadas, cada uma com granularidade de detecção diferente:
Camada 1: Limites de taxa baseados em IP
A abordagem mais comum. O servidor rastreia pedidos por endereço IP dentro de uma janela de tempo. Excedeu o limite e você recebe HTTP 429 (Muitos Pedidos) ou respostas 503.
# Typical rate limit behavior
Request 1-50: HTTP 200 (normal)
Request 51: HTTP 429 (rate limited)
Wait 60 seconds...
Request 52: HTTP 200 (reset)Camada 2: Limites de Sessão/Baseado em Cookie
As faixas solicitam frequência por sessão ou cookie do navegador. Mesmo que você gire IPs, o mesmo token de sessão que atinge o servidor rapidamente irá desencadear limites.
Camada 3: Limites baseados na conta
Para sites que necessitam de login, os limites estão ligados à conta de usuário, independentemente do IP. Comum em APIs e plataformas SaaS.
Camada 4: Análise comportamental
Sistemas avançados como Cloudflare, PerimeterX e Akamai analisam padrões comportamentais: tempo de solicitação, fluxo de navegação, movimentos do mouse (em contextos de navegador). Esta camada é a mais difícil de contornar porque não depende de contadores simples.
Sinais comuns de detecção de limites de taxa
Sites usam vários sinais simultaneamente para detectar raspagem automatizada:
| Sinal | O que Detecta | Dificuldade de Evadir |
|---|---|---|
| Pedidos por IP por minuto | Velocidade bruta | Fácil (utilizar proxies) |
| Pedidos por IP por hora/dia | Volume mantido | Médio (IPs rotativos) |
| Pedido de regularidade do calendário | Intervalos tipo máquina | Médio (adicionar jitter) |
| Faltam/erram os cabeçalhos | Clientes não navegadores | Fácil (definir cabeçalhos apropriados) |
| Padrões de URL sequenciais | Rastreamento sistemático | Médio (ordem aleatória) |
| Impressões digitais TLS | Biblioteca vs navegador | Difícil (use navegadores reais) |
| Execução em JavaScript | Navegador sem cabeça | Difícil (configuração avançada) |
| Eventos do mouse/teclado | Comportamento do Bot | Muito difícil. |
Saiba mais sobre mecanismos de detecção em nosso guia sobre Como sistemas antibot detectam proxies.
Códigos de resposta HTTP que limitam a taxa de sinal
Saber quais códigos HTTP indicam limitação de taxa ajuda você a construir lógica de repetição adequada:
| Código | Significado | Acção |
|---|---|---|
| 200 (com CAPTCHA) | Bloco macio — página de desafio servida | Rodar o IP, abrandar |
| 403 Proibido | IP ou sessão bloqueada | Rodar o IP imediatamente |
| 429 Muitos Pedidos | Limite de taxa explícito atingido | Espere e tente novamente com retrocesso |
| 503 Serviço Indisponível | Sobrecarga ou bloqueio do servidor | Recuar, verificar se bloqueado |
| 302/307 para o URL CAPTCHA | Reencaminhar o desafio | Rodar IP, reduzir a velocidade |
Estratégia 1: Esforço Respeitoso
A abordagem mais simples — mantenha sua taxa de solicitação bem abaixo do que o alvo permite. Isso significa menos falhas, menos largura de banda desperdiçada e raspagem mais sustentável.
import requests
import time
import random
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
def respectful_scrape(urls: list[str], rpm_limit: int = 10) -> list[str]:
"""Scrape URLs while respecting a requests-per-minute limit."""
delay = 60.0 / rpm_limit
results = []
for url in urls:
try:
resp = requests.get(
url,
proxies={"http": PROXY, "https": PROXY},
timeout=30
)
results.append(resp.text if resp.status_code == 200 else None)
except requests.RequestException:
results.append(None)
# Add delay with random jitter (±30%) to look less robotic
jitter = delay * random.uniform(0.7, 1.3)
time.sleep(jitter)
return resultsEstratégia 2: Esforço Adaptativo
Em vez de uma taxa fixa, ajuste dinamicamente sua velocidade com base nas respostas que você recebe. Acelerar quando tudo funcionar, abrandar quando vires sinais de aviso.
Implementação em Python
import requests
import time
import random
from dataclasses import dataclass, field
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
@dataclass
class AdaptiveThrottle:
"""Automatically adjusts request rate based on server responses."""
base_delay: float = 2.0 # seconds between requests
min_delay: float = 0.5
max_delay: float = 30.0
current_delay: float = 2.0
success_streak: int = 0
warning_codes: set = field(default_factory=lambda: {429, 403, 503})
def on_success(self):
self.success_streak += 1
# Speed up after 10 consecutive successes
if self.success_streak >= 10:
self.current_delay = max(self.current_delay * 0.85, self.min_delay)
self.success_streak = 0
def on_rate_limit(self):
self.success_streak = 0
# Double the delay on rate limit
self.current_delay = min(self.current_delay * 2.0, self.max_delay)
def on_block(self):
self.success_streak = 0
# Aggressive backoff on block
self.current_delay = min(self.current_delay * 3.0, self.max_delay)
def wait(self):
jitter = self.current_delay * random.uniform(0.7, 1.3)
time.sleep(jitter)
def scrape_adaptive(urls: list[str]) -> list[dict]:
throttle = AdaptiveThrottle()
results = []
for url in urls:
try:
resp = requests.get(
url,
proxies={"http": PROXY, "https": PROXY},
timeout=30
)
if resp.status_code == 200:
throttle.on_success()
results.append({"url": url, "status": 200, "body": resp.text})
elif resp.status_code == 429:
throttle.on_rate_limit()
# Check Retry-After header
retry_after = int(resp.headers.get("Retry-After", 0))
if retry_after:
time.sleep(retry_after)
results.append({"url": url, "status": 429, "body": None})
elif resp.status_code == 403:
throttle.on_block()
results.append({"url": url, "status": 403, "body": None})
else:
results.append({"url": url, "status": resp.status_code, "body": resp.text})
except requests.RequestException as e:
throttle.on_block()
results.append({"url": url, "status": 0, "error": str(e)})
throttle.wait()
print(f"Current delay: {throttle.current_delay:.1f}s")
return resultsImplementação Node.js
const HttpsProxyAgent = require('https-proxy-agent');
const fetch = require('node-fetch');
class AdaptiveThrottle {
constructor() {
this.currentDelay = 2000; // ms
this.minDelay = 500;
this.maxDelay = 30000;
this.successStreak = 0;
}
onSuccess() {
this.successStreak++;
if (this.successStreak >= 10) {
this.currentDelay = Math.max(this.currentDelay * 0.85, this.minDelay);
this.successStreak = 0;
}
}
onRateLimit() {
this.successStreak = 0;
this.currentDelay = Math.min(this.currentDelay * 2, this.maxDelay);
}
onBlock() {
this.successStreak = 0;
this.currentDelay = Math.min(this.currentDelay * 3, this.maxDelay);
}
async wait() {
const jitter = this.currentDelay * (0.7 + Math.random() * 0.6);
return new Promise(resolve => setTimeout(resolve, jitter));
}
}
async function scrapeAdaptive(urls) {
const throttle = new AdaptiveThrottle();
const agent = new HttpsProxyAgent('http://USERNAME:PASSWORD@gate.proxyhat.com:8080');
const results = [];
for (const url of urls) {
try {
const res = await fetch(url, { agent, timeout: 30000 });
if (res.ok) {
throttle.onSuccess();
results.push({ url, status: res.status, body: await res.text() });
} else if (res.status === 429) {
throttle.onRateLimit();
const retryAfter = parseInt(res.headers.get('retry-after') || '0');
if (retryAfter) await new Promise(r => setTimeout(r, retryAfter * 1000));
results.push({ url, status: 429, body: null });
} else if (res.status === 403) {
throttle.onBlock();
results.push({ url, status: 403, body: null });
}
} catch (err) {
throttle.onBlock();
results.push({ url, status: 0, error: err.message });
}
await throttle.wait();
console.log(`Current delay: ${throttle.currentDelay.toFixed(0)}ms`);
}
return results;
}Estratégia 3: Limitação da Taxa Distribuída
Ao executar várias instâncias de raspador em paralelo, coordene o limite de taxa em todos os trabalhadores. Sem coordenação, cada trabalhador respeita o seu próprio limite, mas o tráfego combinado continua a sobrecarregar o objectivo.
import requests
import time
import threading
class DistributedRateLimiter:
"""Thread-safe rate limiter for multiple scraper workers."""
def __init__(self, max_rpm: int):
self.min_interval = 60.0 / max_rpm
self.lock = threading.Lock()
self.last_request_time = 0.0
def acquire(self):
"""Block until it is safe to make the next request."""
with self.lock:
now = time.time()
elapsed = now - self.last_request_time
if elapsed < self.min_interval:
time.sleep(self.min_interval - elapsed)
self.last_request_time = time.time()
# Shared limiter across all threads
limiter = DistributedRateLimiter(max_rpm=30)
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
def worker(urls: list[str], results: list):
for url in urls:
limiter.acquire()
try:
resp = requests.get(
url,
proxies={"http": PROXY, "https": PROXY},
timeout=30
)
results.append({"url": url, "status": resp.status_code})
except Exception as e:
results.append({"url": url, "error": str(e)})Estratégia 4: Solicitar fila com prioridade
Para projetos complexos de raspagem, use uma fila de prioridades que gere limites de taxa por domínio alvo:
import requests
import time
import heapq
import threading
from collections import defaultdict
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
class DomainRateLimiter:
"""Per-domain rate limiting with priority queue."""
def __init__(self, default_rpm: int = 10):
self.default_rpm = default_rpm
self.domain_limits = {} # domain -> max RPM
self.domain_last = defaultdict(float) # domain -> last request time
self.lock = threading.Lock()
def set_limit(self, domain: str, rpm: int):
self.domain_limits[domain] = rpm
def wait_for_domain(self, domain: str):
rpm = self.domain_limits.get(domain, self.default_rpm)
min_interval = 60.0 / rpm
with self.lock:
now = time.time()
elapsed = now - self.domain_last[domain]
if elapsed < min_interval:
time.sleep(min_interval - elapsed)
self.domain_last[domain] = time.time()
# Configure per-domain limits
limiter = DomainRateLimiter(default_rpm=10)
limiter.set_limit("amazon.com", 3) # Very conservative for Amazon
limiter.set_limit("example.com", 30) # Lenient for simple sites
limiter.set_limit("google.com", 5) # Moderate for GoogleLendo Robots.txt para Dica de Taxa
Muitos sites publicam suas preferências de rastreamento em robots.txt. A Crawl-delay diretiva diz-lhe os segundos mínimos entre os pedidos:
import requests
from urllib.parse import urlparse
from urllib.robotparser import RobotFileParser
def get_crawl_delay(base_url: str, user_agent: str = "*") -> float | None:
"""Extract Crawl-delay from robots.txt."""
parsed = urlparse(base_url)
robots_url = f"{parsed.scheme}://{parsed.netloc}/robots.txt"
try:
resp = requests.get(robots_url, timeout=10)
if resp.status_code != 200:
return None
rp = RobotFileParser()
rp.parse(resp.text.splitlines())
delay = rp.crawl_delay(user_agent)
return delay
except Exception:
return None
# Check before scraping
delay = get_crawl_delay("https://example.com")
if delay:
print(f"Site requests {delay}s between requests")
else:
print("No crawl-delay specified")Erros de limite de taxa comum
- Ignorando 429 respostas. Muitos raspadores tratam todas as respostas não-200 da mesma forma. Um 429 diz exatamente o que aconteceu — use o cabeçalho Retry-After e afaste-se.
- Atrasos fixos sem nervosismo. Um pedido a cada 2.000 segundos parece robótico. Adicione variações aleatórias (nervoso) aos seus atrasos.
- Não coordenando trabalhadores paralelos. Cinco trabalhadores cada fazendo 10 RPM é igual a 50 RPM total. Use um limitador de taxa compartilhado.
- Rotativa IPs sem desacelerar. A rotação IP lhe dá tempo, mas se cada novo IP imediatamente martelar o site, a detecção avançada ainda irá pegá-lo. Combine rotação com estrangulamento adequado.
- Raspar durante as horas de pico. Os locais são mais agressivos com limitação de taxa durante períodos de alto tráfego. Marcar rastejos pesados durante horas fora do pico para o fuso horário do alvo.
Para calcular quantos proxies você precisa para suportar sua raspagem limitada por taxa, consulte Quantas proxies você precisa para raspar?. Para estratégias de rotação proxy que complementam taxa limitante, leia Estratégias de rotação proxy para raspagem de grande escala.
Comece a raspar corretamente com taxa limitada usando o ProxyHat Python SDK ou explorar Planos de preços para o teu projecto.
Perguntas Frequentes
O que acontece quando eu exceder um limite de taxa?
A resposta depende do site. A maioria retorna HTTP 429 com um cabeçalho Retry-After. Alguns servem CAPTCHAs. Sites agressivos bloqueiam imediatamente o IP com uma resposta 403. Na pior das hipóteses, repetidas violações levam a proibições permanentes de PI.
Como posso encontrar um limite de taxa de site?
Comece devagar e aumente gradualmente enquanto monitora os códigos de resposta. Verifique robots.txt para as diretivas Crawl-delay. Observar os cabeçalhos de resposta para os campos X-RateLimit-Limit e X-RateLimit-Remaining. Algumas APIs publicam seus limites na documentação.
O uso de proxies bypass rate limita?
Proxies distribuem solicitações em vários IPs, então cada IP permanece abaixo do limite por IP. No entanto, sites sofisticados também rastreiam sessões, impressões digitais e padrões comportamentais. As proxies são necessárias, mas não são suficientes — combine - as com padrões de solicitação adequados e realistas.
Qual é a taxa de solicitação mais segura para raspar?
Não há resposta universal. Para alvos agressivos como Google ou Amazon, 1-5 pedidos por minuto por IP é seguro. Para sites levemente protegidos, 20-60 RPM por IP pode funcionar. Comece sempre conservador e aumente com base nas taxas de sucesso observadas.






