Rate Limits de Scraping Explicados

Como os limites de taxa funcionam, como os sites detectam raspadores e estratégias práticas para permanecer sob limites. Inclui código de estrangulamento adaptativo e padrões de limitação de taxa distribuídos.

Rate Limits de Scraping Explicados

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:

Sinais comuns de detecção de limites de taxa
SinalO que DetectaDificuldade de Evadir
Pedidos por IP por minutoVelocidade brutaFácil (utilizar proxies)
Pedidos por IP por hora/diaVolume mantidoMédio (IPs rotativos)
Pedido de regularidade do calendárioIntervalos tipo máquinaMédio (adicionar jitter)
Faltam/erram os cabeçalhosClientes não navegadoresFácil (definir cabeçalhos apropriados)
Padrões de URL sequenciaisRastreamento sistemáticoMédio (ordem aleatória)
Impressões digitais TLSBiblioteca vs navegadorDifícil (use navegadores reais)
Execução em JavaScriptNavegador sem cabeçaDifícil (configuração avançada)
Eventos do mouse/tecladoComportamento do BotMuito 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ódigos de resposta HTTP que limitam a taxa de sinal
CódigoSignificadoAcção
200 (com CAPTCHA)Bloco macio — página de desafio servidaRodar o IP, abrandar
403 ProibidoIP ou sessão bloqueadaRodar o IP imediatamente
429 Muitos PedidosLimite de taxa explícito atingidoEspere e tente novamente com retrocesso
503 Serviço IndisponívelSobrecarga ou bloqueio do servidorRecuar, verificar se bloqueado
302/307 para o URL CAPTCHAReencaminhar o desafioRodar 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 results

Estraté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 results

Implementaçã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 Google

Lendo 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.

Pronto para começar?

Acesse mais de 50M de IPs residenciais em mais de 148 países com filtragem por IA.

Ver preçosProxies residenciais
← Voltar ao Blog