Se a conta do proxy está crescendo mais rápido do que o volume de dados coletados, o problema quase sempre está na lógica de retry. O parser silenciosamente repete solicitações malsucedidas várias vezes, consumindo tráfego em timeouts e captchas, enquanto o desenvolvedor nem vê esses gastos nos logs. Vamos analisar como calcular as perdas reais e reduzi-las sem perder a qualidade dos dados.
Por que a lógica de retry consome tráfego
A maioria dos parsers é escrita com uma lógica de retry ingênua: se a solicitação falhar — repetir, e assim por 3-5 vezes. O problema é que cada solicitação repetida não é apenas uma nova solicitação HTTP, mas um ciclo completo: handshake TCP, negociação TLS, carregamento da página inteira (mesmo que apenas um bloco de dados seja necessário), e às vezes até o recarregamento de imagens ou arquivos JS, se o parser usar um navegador headless em vez de um simples cliente HTTP.
As repetições são especialmente caras ao trabalhar com proxies residenciais, onde o tráfego é tarifado por volume, e não pelo número de solicitações. Uma solicitação malsucedida para uma página de produto da Wildberries com imagens e scripts pode custar 300-500 KB. Se o parser faz 3 repetições em caso de timeout, você paga pela mesma solicitação malsucedida quatro vezes seguidas — e isso sem garantia de que a quarta tentativa será bem-sucedida.
A segunda razão é o retry em erros que não podem ser corrigidos com uma repetição. Se o site retornou 403 devido à detecção de um bot, a solicitação repetida com a mesma impressão digital e a mesma sessão de cookies quase certamente receberá a mesma resposta. O parser consome tráfego em tentativas que matematicamente não podem terminar em sucesso, a menos que o endereço IP ou a impressão digital do navegador mudem.
Quanto tráfego realmente vai para repetições
Para entender a magnitude do problema, vamos pegar um exemplo simples. O parser coleta cartões de produtos de um marketplace, o tamanho médio da resposta é de 250 KB (HTML + JSON API + parte da estática). Em um funcionamento estável sem bloqueios, a proporção de solicitações malsucedidas se mantém em torno de 5-8%. Mas em uma coleta agressiva através de proxies baratos de data center, esse número pode subir para 25-35%, porque o provedor-alvo rapidamente reconhece o padrão e começa a retornar captchas ou banimentos temporários por IP.
Vamos calcular com números. Suponha que precisamos coletar 100.000 cartões de produtos:
| Proporção de solicitações malsucedidas | Repetições por solicitação (em média) | Tráfego total | Excesso de consumo |
|---|---|---|---|
| 5% | 0.15 | 28.75 GB | +15% |
| 15% | 0.45 | 36.25 GB | +45% |
| 30% | 0.90 | 47.5 GB | +90% |
Como pode ser visto, com uma proporção de falhas de 30% e uma estratégia de retry de "repetir até 3 vezes", o tráfego real quase dobra em relação ao mínimo teórico de 25 GB. Esses mais de 20 GB extras são perdas diretas no orçamento de proxies, que podem ser reduzidas se a lógica de repetições for revista.
Erros comuns na lógica de retry dos parsers
Antes de corrigir a lógica de retry, é importante reconhecer os antipadrões típicos que ocorrem em 90% dos parsers feitos à mão:
- Retry sem analisar o código de erro. A repetição é acionada para qualquer situação anômala — 403, 429, 500, timeout, desconexão — embora a estratégia de tratamento para elas deva ser diferente.
- Atraso fixo entre repetições. Por exemplo, 2 segundos entre tentativas, independentemente de ser a primeira ou a quinta tentativa — isso é ou muito agressivo para o site, ou muito lento para grandes volumes.
- Repetição com o mesmo IP e a mesma sessão. Se o site bloqueou a solicitação por impressão digital, repetir com parâmetros idênticos não muda o resultado, mas consome tráfego.
- Sem limite superior de tentativas. Alguns parsers ficam presos em URLs "mortas" e fazem dezenas de repetições antes de desistir.
- Falta de distinção entre erros temporários e permanentes. 404 (página não existe) e 503 (servidor temporariamente indisponível) requerem lógicas diferentes — mas muitas vezes são tratados da mesma forma.
Backoff exponencial com código em Python
Uma solução simples, mas eficaz, é o atraso exponencial com jitter (variação aleatória), que reduz o número de repetições sem sentido e distribui a carga ao longo do tempo. Em vez de uma pausa fixa entre tentativas, o atraso cresce exponencialmente, dando ao site tempo para "esfriar" após um bloqueio, e ao próprio parser — não gastar tráfego em solicitações que quase certamente falharão.
import time
import random
import requests
def fetch_with_backoff(url, proxies, max_retries=4, base_delay=1.0):
retryable_codes = {429, 500, 502, 503, 504}
non_retryable_codes = {404, 410}
for attempt in range(max_retries + 1):
try:
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response
if response.status_code in non_retryable_codes:
# não faz sentido repetir — a página fisicamente não existe
return None
if response.status_code not in retryable_codes:
return None
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
pass # erro de rede temporário — pode repetir
if attempt == max_retries:
return None
# atraso exponencial com jitter
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
return None
A ideia chave deste código é a separação de erros em três categorias: aqueles que não podem ser corrigidos com repetição (404, 410), aqueles que podem ser repetidos com atraso (429, 500-504, timeouts), e tudo o mais que é imediatamente considerado uma falha sem gastos em tentativas adicionais. Essa separação por si só reduz o tráfego desnecessário em 20-30% em comparação com a ingênua "repetir tudo".
Dica: adicione o cabeçalho Retry-After ao tratamento —
muitos sites indicam quantos segundos esperar antes de repetir. Ignorar esse cabeçalho é
uma causa comum de banimentos e tráfego desnecessário.
Circuit breaker: quando parar
O backoff exponencial ajuda no nível de uma solicitação, mas não protege contra a situação em que todo o domínio ou um proxy específico está temporariamente indisponível para centenas de URLs consecutivas. Aqui, é necessário um padrão de circuit breaker — um "disjuntor" que monitora a proporção de erros durante um período recente e interrompe temporariamente as tentativas se essa proporção exceder um limite, em vez de continuar batendo em uma porta fechada.
class CircuitBreaker:
def __init__(self, failure_threshold=0.5, window_size=50, cooldown=60):
self.failure_threshold = failure_threshold
self.window_size = window_size
self.cooldown = cooldown
self.results = []
self.open_until = 0
def is_open(self):
return time.time() < self.open_until
def record(self, success: bool):
self.results.append(success)
if len(self.results) > self.window_size:
self.results.pop(0)
if len(self.results) == self.window_size:
failure_rate = 1 - sum(self.results) / self.window_size
if failure_rate > self.failure_threshold:
self.open_until = time.time() + self.cooldown
self.results.clear()
A lógica é simples: se mais da metade das últimas 50 solicitações falharam, o parser suspende as tentativas para esse domínio ou proxy por 60 segundos. Durante esse tempo, é possível mudar o IP, reduzir a velocidade das solicitações ou alternar para outro pool de proxies. Isso é especialmente importante ao trabalhar com sites-alvo que temporariamente banem um intervalo de IPs após exceder a frequência de solicitações — continuar batendo em uma porta fechada significa apenas queimar tráfego em vão.
Rotação inteligente de proxies em repetições
Uma das medidas mais eficazes contra retries desnecessários é não repetir a solicitação com o mesmo IP que já recebeu uma recusa. A lógica é simples: se o erro está relacionado ao bloqueio por IP (403, 429, redirecionamento para captcha), mudar o proxy antes da repetição aumenta drasticamente a chance de sucesso e reduz o número de tentativas.
Para a coleta de dados de marketplaces como Wildberries, Ozon ou Avito, uma boa combinação é: solicitações normais vão através de proxies de data center — eles são mais rápidos e baratos, e assim que o detector de bloqueio dispara várias vezes seguidas, o parser muda para proxies residenciais, que raramente caem em filtros de sistemas anti-bots. Essa abordagem híbrida reduz o consumo total de tráfego, pois os caros IPs residenciais são usados apenas onde realmente necessário, e não para todas as solicitações consecutivas.
| Tipo de erro | Estratégia de repetição | Mudança de IP necessária? |
|---|---|---|
| Timeout de conexão | Backoff, 1-2 repetições | Não |
| 403 / captcha | Rotação imediata | Sim, obrigatoriamente |
| 429 (limite de taxa) | Backoff com Retry-After | Desejável |
| 500-503 | Backoff, 2-3 repetições | Não |
| 404 / 410 | Sem repetições | — |
Para parsers que emulam tráfego móvel (por exemplo, coleta de dados de versões móveis de aplicativos de marketplaces ou redes sociais), faz sentido usar proxies móveis — eles raramente levantam suspeitas nos sistemas de proteção, justamente porque os operadores de telecomunicações atribuem os mesmos IPs a milhares de usuários reais simultaneamente, e o bloqueio pontual se torna inviável para o site-alvo.
Monitoramento de métricas de retry
Sem métricas, a otimização da lógica de retry se torna um palpite. O conjunto mínimo de indicadores que deve ser registrado em cada solicitação:
- Taxa de retry — proporção de solicitações que exigiram pelo menos uma repetição.
- Sucesso após retry — qual porcentagem de repetições acabou sendo bem-sucedida (se esse indicador for baixo, as repetições estão apenas queimando tráfego).
- Tráfego para resultado bem-sucedido — volume total de dados transferidos, dividido pelo número de registros coletados com sucesso. Essa é a métrica chave de eficiência.
- Distribuição de erros por códigos — ajuda a entender onde ocorre a principal perda de tráfego: timeouts, 403, 429 ou algo mais.
- Taxa de retry por proxies específicos — se um IP tem uma taxa de retry de 80%, enquanto os outros têm 10%, o problema é local e pode ser resolvido mudando apenas aquele nó, e não toda a lógica.
Mesmo uma tabela simples no Google Sheets ou um log em CSV com essas cinco métricas, atualizada a cada hora, fornece dados suficientes para identificar anomalias e ajustar a estratégia a tempo — por exemplo, reduzindo a frequência de solicitações para uma seção específica do site ou aumentando a proporção de IPs residenciais no pool.
Checklist de otimização de tráfego em retry
- Separe os códigos de erro em retryable e non-retryable — não repita 404/410.
- Implemente um backoff exponencial com jitter em vez de um atraso fixo.
- Respeite o cabeçalho
Retry-After, se o site o enviar. - Mude o IP antes da repetição em caso de 403 e suspeita de detecção de bot.
- Estabeleça um limite rígido no número de tentativas (geralmente 3-4 é suficiente).
- Implemente um circuit breaker para domínios e nós de proxy com alta taxa de falhas.
- Registre a taxa de retry e o tráfego para resultados bem-sucedidos — sem métricas, a otimização é impossível.
- Separe o pool de proxies: data centers baratos para áreas estáveis, IPs residenciais ou móveis para áreas problemáticas.
Conclusão
A lógica de retry não é um pequeno detalhe do parser, mas um dos principais fatores que afetam o custo da coleta de dados. Uma estratégia ingênua de "repetir tudo" pode aumentar o tráfego real em 40-90% em comparação com o mínimo teórico, enquanto a maior parte das repetições termina na mesma falha que a primeira tentativa. A separação de erros por tipos, o backoff exponencial, o circuit breaker e a rotação inteligente de IPs permitem reduzir essas perdas drasticamente sem diminuir a completude dos dados coletados.
Se o seu parser trabalha com sites que detectam bots de forma agressiva — marketplaces, redes sociais, plataformas de publicidade — vale a pena combinar vários tipos de proxies dependendo da tarefa. Para operações básicas, proxies de data center são adequados, enquanto onde é necessária a máxima resistência a bloqueios, proxies residenciais com IPs reais de usuários comuns são a melhor escolha. Essa abordagem híbrida, combinada com uma lógica de retry bem estruturada, resulta em uma redução significativa do consumo de tráfego mantendo o mesmo volume de dados coletados.