← Voltar ao blog

Filtragem de proxies ruins no pool do parser: métricas de erro e rotação em Python

Analisamos quais métricas de erro sinalizam a degradação do proxy no pool do parser e como construir um sistema automático de rejeição e rotação de IPs.

📅19 de outubro de 2026

Se o parser Wildberries ou Ozon de repente começou a retornar páginas vazias, timeouts e erros 403 — o problema nem sempre está no próprio site. Na maioria das vezes, parte dos endereços IP no pool de proxies já está "vencida": eles foram banidos, desacelerados ou estão sob CAPTCHA. Vamos analisar quais métricas diferenciam um proxy funcional de um morto e como automatizar a filtragem sem controle manual.

Por que o pool de proxies degrada com o tempo

Qualquer pool de proxies — mesmo os comprados de um provedor confiável — perde parte dos endereços funcionais com o tempo. As razões são diversas: sistemas antifraude do Wildberries, Ozon e Avito acumulam marcas de reputação nos IPs, parte dos endereços entra em listas negras após solicitações em massa de outros clientes do provedor (se o proxy for compartilhado), faixas de datacenters são banidas em lotes por sub-redes, e IPs residenciais às vezes simplesmente saem do pool porque o usuário real desligou o dispositivo.

Se o pool não for limpo, a proporção de proxies "mortos" cresce de forma não linear: inicialmente 5%, após uma semana de parsing ativo — 20-30%. O parser começa a gastar recursos em tentativas repetidas através de IPs não funcionais, aumentando o tempo médio para coletar uma página, e nos logs se acumulam erros que mascaram problemas reais (por exemplo, mudanças no layout do site). Sem um sistema de filtragem, você mantém o pool inchado "para reserva" ou recebe falhas regulares na coleta de dados.

A tarefa não é apenas registrar o fato do erro, mas classificá-lo: sobrecarga temporária do servidor, banimento de um IP específico, bloqueio regional ou problema do lado do parser (cabeçalhos incorretos, cookies desatualizados). O tipo de erro determina se o proxy deve ser removido do pool permanentemente, colocado em "quarentena" por um tempo ou se deve apenas repetir a solicitação.

Métricas-chave de erro para filtragem

Para diferenciar uma falha temporária de um proxy realmente morto, é necessário coletar várias métricas para cada IP, em vez de se basear em um único status HTTP. Aqui está um conjunto básico que deve ser registrado a cada solicitação:

Métrica O que sinaliza Ação
HTTP 403 / 429 Banimento ou limite de taxa no IP Quarentena de 30-60 minutos, se repetir — remoção
Timeout de conexão Proxy sobrecarregado ou morto 3 timeouts consecutivos — remoção do pool
CAPTCHA na resposta IP sob suspeita do anti-bot Reduzir a frequência das solicitações ou substituir o IP
Tempo de resposta > 5 seg Degradação da velocidade do lado do proxy Reduzir prioridade no pool
Tamanho da resposta abaixo da média Retornada uma página de bloqueio Verificação adicional do conteúdo antes da aceitação
Taxa de solicitações bem-sucedidas (success rate) Avaliação integral da qualidade do IP Abaixo de 70% em 50 solicitações — remoção

É importante não confundir códigos de erro do site com erros de rede. Por exemplo, 404 — não é um problema do proxy, mas sim um produto ausente no Wildberries. Mas se o mesmo IP recebe regularmente 403 onde outros IPs recebem 200 — isso é um sinal claro de banimento pontual desse endereço.

Algoritmo de pontuação de proxies

Em vez de um binário "funcional / não funcional", é mais conveniente calcular para cada IP uma pontuação de 0 a 100, que diminui gradualmente em caso de erros e se recupera em solicitações bem-sucedidas. Isso permite não descartar proxies após uma falha aleatória (o servidor do marketplace pode ter simplesmente travado por um segundo), mas rapidamente remover aqueles IPs que sistematicamente retornam lixo.

A lógica é simples: uma solicitação bem-sucedida com código 200 e conteúdo válido adiciona +2 à pontuação (mas não acima de 100). Timeout — menos 15. Código 403/429 — menos 25. CAPTCHA na resposta — menos 20. Se a pontuação cair abaixo do limiar (por exemplo, 30), o proxy vai para a quarentena por um tempo fixo, e se após retornar da quarentena a pontuação cair novamente abaixo de 10 — o IP é removido do pool permanentemente e registrado para análise.

Essa abordagem é semelhante ao sistema de reputação em navegadores anti-detect como Dolphin Anty ou AdsPower, onde contas têm "saúde" dependendo da atividade. A mesma mecânica funciona bem para endereços IP no parsing.

Código: ProxyPool com filtragem automática em Python

Abaixo está a estrutura funcional da classe para gerenciar o pool de proxies considerando as métricas de erro. Ela pode ser conectada a qualquer parser usando requests, httpx ou Scrapy, substituindo a lógica de obtenção de proxies pela chamada pool.get_proxy().

import time
import random
from dataclasses import dataclass, field
from typing import Optional

@dataclass
class ProxyStats:
    address: str
    score: int = 100
    quarantine_until: float = 0.0
    total_requests: int = 0
    total_success: int = 0
    consecutive_timeouts: int = 0
    is_dead: bool = False

    @property
    def success_rate(self) -> float:
        if self.total_requests == 0:
            return 1.0
        return self.total_success / self.total_requests


class ProxyPool:
    def __init__(self, proxies: list[str],
                 min_score: int = 30,
                 dead_score: int = 10,
                 quarantine_seconds: int = 1800):
        self.stats = {p: ProxyStats(address=p) for p in proxies}
        self.min_score = min_score
        self.dead_score = dead_score
        self.quarantine_seconds = quarantine_seconds

    def _is_available(self, stats: ProxyStats) -> bool:
        if stats.is_dead:
            return False
        if stats.quarantine_until > time.time():
            return False
        return True

    def get_proxy(self) -> Optional[str]:
        available = [s for s in self.stats.values() if self._is_available(s)]
        if not available:
            return None
        # ordenamos pela pontuação, pegamos um aleatório dos 30% melhores, para não sobrecarregar um IP
        available.sort(key=lambda s: s.score, reverse=True)
        top_pool = available[:max(1, len(available) // 3)]
        return random.choice(top_pool).address

    def report_success(self, proxy: str):
        s = self.stats[proxy]
        s.total_requests += 1
        s.total_success += 1
        s.consecutive_timeouts = 0
        s.score = min(100, s.score + 2)

    def report_timeout(self, proxy: str):
        s = self.stats[proxy]
        s.total_requests += 1
        s.consecutive_timeouts += 1
        s.score -= 15
        if s.consecutive_timeouts >= 3:
            self._quarantine(s)
        self._check_dead(s)

    def report_blocked(self, proxy: str, captcha: bool = False):
        s = self.stats[proxy]
        s.total_requests += 1
        s.score -= 20 if captcha else 25
        self._quarantine(s)
        self._check_dead(s)

    def _quarantine(self, s: ProxyStats):
        if s.score < self.min_score:
            s.quarantine_until = time.time() + self.quarantine_seconds

    def _check_dead(self, s: ProxyStats):
        if s.score <= self.dead_score:
            s.is_dead = True

    def stats_report(self) -> list[dict]:
        return [
            {
                "proxy": s.address,
                "score": s.score,
                "success_rate": round(s.success_rate, 2),
                "dead": s.is_dead,
            }
            for s in self.stats.values()
        ]

Em um parser real, as chamadas report_success, report_timeout e report_blocked são inseridas diretamente no manipulador de resposta após cada solicitação HTTP. Exemplo de integração com requests:

import requests

def fetch(url: str, pool: ProxyPool):
    proxy = pool.get_proxy()
    if not proxy:
        raise RuntimeError("Não há proxies disponíveis no pool")

    proxies = {"http": proxy, "https": proxy}
    try:
        resp = requests.get(url, proxies=proxies, timeout=8)
    except requests.exceptions.Timeout:
        pool.report_timeout(proxy)
        return None

    if resp.status_code in (403, 429):
        pool.report_blocked(proxy)
        return None

    if "captcha" in resp.text.lower():
        pool.report_blocked(proxy, captcha=True)
        return None

    pool.report_success(proxy)
    return resp

Essa abordagem alivia a carga do monitoramento manual: você configura os limiares uma vez, e depois o pool se "recupera" automaticamente, expulsando endereços mortos e retornando da quarentena aqueles que estavam temporariamente inativos.

Valores de limiar para banimento de IPs

Não existem números universais — os limiares dependem da plataforma e da agressividade do seu anti-bot. Mas há referências que funcionam bem na prática ao fazer parsing do Wildberries, Ozon e Avito:

  • Taxa de sucesso abaixo de 70% em uma amostra de 30-50 solicitações — motivo para remover o IP, mesmo que a pontuação atual ainda não seja crítica.
  • 3 timeouts consecutivos — quase sempre significa que o proxy está fisicamente indisponível ou sobrecarregado, não faz sentido mantê-lo no pool ativo.
  • CAPTCHA mais de 1 vez a cada 20 solicitações — o IP já está sob suspeita do anti-bot, é melhor reduzir a frequência de acessos ou substituir o endereço.
  • Tempo médio de resposta acima de 2x da mediana do pool — sinal de degradação, não crítico, mas motivo para reduzir a prioridade na escolha.

Uma quarentena de 30-60 minutos geralmente é suficiente para proxies de datacenter e residenciais, para que o anti-bot "esqueça" a atividade suspeita temporária. Para proxies móveis, a situação é diferente: lá o IP muda quando a operadora troca a célula, portanto a quarentena pode ser mais curta — 10-15 minutos, pois após a rotação já é um endereço fisicamente diferente.

Healthcheck e rotação em segundo plano

Além da filtragem reativa (com base em erros no fluxo principal de parsing), é útil executar um healthcheck em segundo plano — um processo separado que, a cada poucos minutos, verifica os proxies com solicitações leves para uma URL neutra (por exemplo, httpbin.org/ip) e atualiza as estatísticas do pool antes que o proxy entre em uma solicitação real para Wildberries ou Ozon.

import threading

def healthcheck_loop(pool: ProxyPool, interval: int = 300):
    while True:
        for proxy in list(pool.stats.keys()):
            try:
                r = requests.get(
                    "https://httpbin.org/ip",
                    proxies={"http": proxy, "https": proxy},
                    timeout=5
                )
                if r.status_code == 200:
                    pool.report_success(proxy)
                else:
                    pool.report_blocked(proxy)
            except requests.exceptions.RequestException:
                pool.report_timeout(proxy)
        time.sleep(interval)

threading.Thread(target=healthcheck_loop, args=(pool,), daemon=True).start()

Esse processo em segundo plano é especialmente importante se o pool for grande (centenas de IPs) e o parsing não for contínuo, mas em lotes — sem o healthcheck, parte dos proxies simplesmente acaba "vencendo" entre as execuções, e os primeiros minutos de parsing são gastos na filtragem ao vivo, em vez de trabalhar imediatamente com endereços verificados.

Qual tipo de proxy escolher para parsing de marketplaces

A escolha do tipo de proxy afeta diretamente a frequência com que será necessário executar a filtragem e quão agressivos serão os limiares. Para um parsing estável do Wildberries e Ozon, onde o anti-bot analisa padrões de comportamento e a reputação da sub-rede, os proxies residenciais se destacam — seus IPs pertencem a provedores reais de internet doméstica, e o anti-bot bloqueia eles em lotes com muito menos frequência.

Para fontes mais simples e menos protegidas (por exemplo, coleta de dados públicos do Avito ou monitoramento de preços em vitrines simples), muitas vezes são suficientes proxies de datacenter — eles são mais rápidos e mais baratos, o que é crítico em grandes volumes de solicitações, mas também são banidos um pouco mais frequentemente, portanto os limiares de filtragem para eles devem ser mais rigorosos.

Tipo de proxy Velocidade de filtragem Quando usar
Datacenter Alta, sub-redes são banidas em lotes Vitrines simples, grandes volumes, baixa proteção
Residencial Média, IPs são banidos pontualmente Wildberries, Ozon, sites com anti-bot
Móvel Baixa, IP muda automaticamente pela operadora Fontes altamente protegidas, parsing de alta frequência

Na prática, muitas equipes utilizam um pool híbrido: a maior parte das solicitações passa por IPs residenciais, e em caso de banimentos em massa, proxies móveis são conectados como reserva "de emergência", pois a troca de IP ocorre naturalmente através da rotação da rede celular e não requer intervenção manual.

Conclusão

Um pool de proxies sem um sistema de filtragem inevitavelmente degrada, e quanto mais tempo ele opera sem limpeza, mais recursos o parser gasta em IPs mortos. Um modelo de pontuação que considera códigos HTTP, timeouts, CAPTCHA e taxa de sucesso permite identificar automaticamente endereços problemáticos e manter o pool em funcionamento sem controle manual. O código deste artigo pode ser adaptado para qualquer stack — requests, httpx, Scrapy ou seu próprio parser assíncrono.

Se você está apenas montando um pool para parsing de marketplaces e deseja reduzir a frequência de banimentos desde o início, recomendamos considerar proxies residenciais — eles oferecem uma taxa de sucesso significativamente mais alta em plataformas protegidas como Wildberries e Ozon em comparação com endereços de datacenter comuns.