Voltar ao blog

Como Corrigir Erros de Timeout ao Usar Proxy

Erros de timeout através de proxy — problema comum em parsing e automação. Analisamos as causas e oferecemos soluções funcionais com exemplos de código.

📅15 de dezembro de 2025

Como corrigir erros de timeout ao trabalhar através de proxy

A requisição travou, o script caiu com o erro TimeoutError, os dados não foram obtidos. Situação familiar? Erros de timeout através de proxy — um dos problemas mais comuns ao fazer parsing e automação. Vamos analisar as causas e fornecer soluções concretas.

Por que ocorrem erros de timeout

Timeout — não é um problema, mas um sintoma. Antes de tratar, é preciso entender a causa:

Servidor proxy lento. Um servidor sobrecarregado ou geograficamente distante adiciona latência a cada requisição. Se seu timeout é 10 segundos, mas o proxy responde em 12 — erro.

Bloqueio no site de destino. O site pode intencionalmente "suspender" requisições suspeitas em vez de recusá-las explicitamente. Esta é uma tática contra bots — manter a conexão aberta indefinidamente.

Problemas com DNS. O proxy deve resolver o domínio. Se o servidor DNS do proxy for lento ou indisponível — a requisição fica pendurada na fase de conexão.

Configuração incorreta de timeouts. Um timeout geral para tudo — erro comum. Connect timeout e read timeout — são coisas diferentes e devem ser configurados separadamente.

Problemas de rede. Perda de pacotes, conexão instável do proxy, problemas de roteamento — tudo isso leva a timeouts.

Tipos de timeouts e sua configuração

A maioria das bibliotecas HTTP suporta vários tipos de timeouts. Compreender a diferença entre eles — é a chave para a configuração correta.

Connect timeout

Tempo para estabelecer conexão TCP com o proxy e servidor de destino. Se o proxy estiver indisponível ou o servidor não responder — este timeout será acionado. Valor recomendado: 5-10 segundos.

Read timeout

Tempo de espera por dados após estabelecer a conexão. O servidor se conectou, mas fica em silêncio — o read timeout será acionado. Para páginas normais: 15-30 segundos. Para APIs pesadas: 60+ segundos.

Total timeout

Tempo total para toda a requisição do início ao fim. Proteção contra conexões travadas. Normalmente: connect + read + margem.

Exemplo de configuração em Python com a biblioteca requests:

import requests

proxies = {
    "http": "http://user:pass@proxy.example.com:8080",
    "https": "http://user:pass@proxy.example.com:8080"
}

# Tupla: (connect_timeout, read_timeout)
timeout = (10, 30)

try:
    response = requests.get(
        "https://target-site.com/api/data",
        proxies=proxies,
        timeout=timeout
    )
except requests.exceptions.ConnectTimeout:
    print("Não foi possível conectar ao proxy ou servidor")
except requests.exceptions.ReadTimeout:
    print("O servidor não enviou dados a tempo")

Para aiohttp (Python assíncrono):

import aiohttp
import asyncio

async def fetch_with_timeout():
    timeout = aiohttp.ClientTimeout(
        total=60,      # Timeout total
        connect=10,    # Para conexão
        sock_read=30   # Para leitura de dados
    )
    
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(
            "https://target-site.com/api/data",
            proxy="http://user:pass@proxy.example.com:8080"
        ) as response:
            return await response.text()

Lógica de retry: abordagem correta

Timeout — nem sempre é um erro fatal. Frequentemente, uma requisição repetida passa com sucesso. Mas retry deve ser feito com inteligência.

Atraso exponencial

Não bombardeie o servidor com requisições repetidas sem pausa. Use backoff exponencial: cada tentativa seguinte — com atraso crescente.

import requests
import time
import random

def fetch_with_retry(url, proxies, max_retries=3):
    """Requisição com retry e atraso exponencial"""
    
    for attempt in range(max_retries):
        try:
            response = requests.get(
                url,
                proxies=proxies,
                timeout=(10, 30)
            )
            response.raise_for_status()
            return response
            
        except (requests.exceptions.Timeout, 
                requests.exceptions.ConnectionError) as e:
            
            if attempt == max_retries - 1:
                raise  # Última tentativa — relançamos o erro
            
            # Atraso exponencial: 1s, 2s, 4s...
            # + jitter aleatório para não criar ondas de requisições
            delay = (2 ** attempt) + random.uniform(0, 1)
            print(f"Tentativa {attempt + 1} falhou: {e}")
            print(f"Repetindo em {delay:.1f} segundos...")
            time.sleep(delay)

Biblioteca tenacity

Para código em produção, é mais conveniente usar soluções prontas:

from tenacity import retry, stop_after_attempt, wait_exponential
from tenacity import retry_if_exception_type
import requests

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10),
    retry=retry_if_exception_type((
        requests.exceptions.Timeout,
        requests.exceptions.ConnectionError
    ))
)
def fetch_data(url, proxies):
    response = requests.get(url, proxies=proxies, timeout=(10, 30))
    response.raise_for_status()
    return response.json()

Rotação de proxy em caso de timeouts

Se um proxy constantemente gera timeouts — o problema está nele. Solução lógica: mudar para outro.

import requests
from collections import deque
from dataclasses import dataclass, field
from typing import Optional
import time

@dataclass
class ProxyManager:
    """Gerenciador de proxy com rastreamento de falhas"""
    
    proxies: list
    max_failures: int = 3
    cooldown_seconds: int = 300
    _failures: dict = field(default_factory=dict)
    _cooldown_until: dict = field(default_factory=dict)
    
    def get_proxy(self) -> Optional[str]:
        """Obter proxy funcionando"""
        current_time = time.time()
        
        for proxy in self.proxies:
            # Pular proxies em cooldown
            if self._cooldown_until.get(proxy, 0) > current_time:
                continue
            return proxy
        
        return None  # Todos os proxies em cooldown
    
    def report_failure(self, proxy: str):
        """Relatar requisição falhada"""
        self._failures[proxy] = self._failures.get(proxy, 0) + 1
        
        if self._failures[proxy] >= self.max_failures:
            # Colocar proxy em cooldown
            self._cooldown_until[proxy] = time.time() + self.cooldown_seconds
            self._failures[proxy] = 0
            print(f"Proxy {proxy} colocado em cooldown")
    
    def report_success(self, proxy: str):
        """Resetar contador de erros ao sucesso"""
        self._failures[proxy] = 0


def fetch_with_rotation(url, proxy_manager, max_attempts=5):
    """Requisição com troca automática de proxy em caso de erros"""
    
    for attempt in range(max_attempts):
        proxy = proxy_manager.get_proxy()
        
        if not proxy:
            raise Exception("Nenhum proxy disponível")
        
        proxies = {"http": proxy, "https": proxy}
        
        try:
            response = requests.get(url, proxies=proxies, timeout=(10, 30))
            response.raise_for_status()
            proxy_manager.report_success(proxy)
            return response
            
        except (requests.exceptions.Timeout, 
                requests.exceptions.ConnectionError):
            proxy_manager.report_failure(proxy)
            print(f"Timeout através de {proxy}, tentando outro...")
            continue
    
    raise Exception(f"Não foi possível obter dados após {max_attempts} tentativas")

Ao usar proxies residenciais com rotação automática, essa lógica é simplificada — o provedor automaticamente alterna o IP a cada requisição ou em intervalo definido.

Requisições assíncronas com controle de timeouts

Em parsing em massa, requisições síncronas são ineficientes. A abordagem assíncrona permite processar centenas de URLs em paralelo, mas requer trabalho cuidadoso com timeouts.

import aiohttp
import asyncio
from typing import List, Tuple

async def fetch_one(
    session: aiohttp.ClientSession, 
    url: str,
    semaphore: asyncio.Semaphore
) -> Tuple[str, str | None, str | None]:
    """Carregamento de uma URL com tratamento de timeout"""
    
    async with semaphore:  # Limitar paralelismo
        try:
            async with session.get(url) as response:
                content = await response.text()
                return (url, content, None)
                
        except asyncio.TimeoutError:
            return (url, None, "timeout")
        except aiohttp.ClientError as e:
            return (url, None, str(e))


async def fetch_all(
    urls: List[str],
    proxy: str,
    max_concurrent: int = 10
) -> List[Tuple[str, str | None, str | None]]:
    """Carregamento em massa com controle de timeouts e paralelismo"""
    
    timeout = aiohttp.ClientTimeout(total=45, connect=10, sock_read=30)
    semaphore = asyncio.Semaphore(max_concurrent)
    
    connector = aiohttp.TCPConnector(
        limit=max_concurrent,
        limit_per_host=5  # Não mais de 5 conexões por host
    )
    
    async with aiohttp.ClientSession(
        timeout=timeout,
        connector=connector
    ) as session:
        # Definir proxy para todas as requisições
        tasks = [
            fetch_one(session, url, semaphore) 
            for url in urls
        ]
        results = await asyncio.gather(*tasks)
    
    # Estatísticas
    success = sum(1 for _, content, _ in results if content)
    timeouts = sum(1 for _, _, error in results if error == "timeout")
    print(f"Sucesso: {success}, Timeouts: {timeouts}")
    
    return results


# Uso
async def main():
    urls = [f"https://example.com/page/{i}" for i in range(100)]
    results = await fetch_all(
        urls, 
        proxy="http://user:pass@proxy.example.com:8080",
        max_concurrent=10
    )

asyncio.run(main())

Importante: Não defina paralelismo muito alto. 50-100 requisições simultâneas através de um proxy — já é muito. Melhor 10-20 com vários proxies.

Diagnóstico: como encontrar a causa

Antes de alterar configurações, determine a fonte do problema.

Passo 1: Verificar proxy diretamente

# Teste simples via curl com medição de tempo
curl -x http://user:pass@proxy:8080 \
     -w "Connect: %{time_connect}s\nTotal: %{time_total}s\n" \
     -o /dev/null -s \
     https://httpbin.org/get

Se time_connect for maior que 5 segundos — problema com proxy ou rede até ele.

Passo 2: Comparar com requisição direta

import requests
import time

def measure_request(url, proxies=None):
    start = time.time()
    try:
        r = requests.get(url, proxies=proxies, timeout=30)
        elapsed = time.time() - start
        return f"OK: {elapsed:.2f}s, status: {r.status_code}"
    except Exception as e:
        elapsed = time.time() - start
        return f"FAIL: {elapsed:.2f}s, error: {type(e).__name__}"

url = "https://target-site.com"
proxy = {"http": "http://proxy:8080", "https": "http://proxy:8080"}

print("Direto:", measure_request(url))
print("Via proxy:", measure_request(url, proxy))

Passo 3: Verificar diferentes tipos de proxy

Timeouts podem depender do tipo de proxy:

Tipo de proxy Latência típica Timeout recomendado
Datacenter 50-200 ms Connect: 5s, Read: 15s
Residencial 200-800 ms Connect: 10s, Read: 30s
Mobile 300-1500 ms Connect: 15s, Read: 45s

Passo 4: Registrar detalhes

import logging
import requests
from requests.adapters import HTTPAdapter

# Ativar logging de debug
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)

# Agora você verá todos os estágios da requisição:
# - Resolução de DNS
# - Estabelecimento de conexão
# - Envio de requisição
# - Recebimento de resposta

Checklist para resolver erros de timeout

Algoritmo breve de ações ao encontrar timeouts:

  1. Determine o tipo de timeout — connect ou read? São problemas diferentes.
  2. Verifique o proxy separadamente — funciona? Qual é a latência?
  3. Aumente os timeouts — talvez os valores sejam muito agressivos para seu tipo de proxy.
  4. Adicione retry com backoff — timeouts únicos são normais, o importante é a resiliência.
  5. Configure rotação — mude automaticamente para outro proxy em caso de problemas.
  6. Limite o paralelismo — muitas requisições simultâneas sobrecarregam o proxy.
  7. Verifique o site de destino — talvez ele esteja bloqueando ou throttle-ando suas requisições.

Conclusão

Erros de timeout através de proxy — problema solucionável. Na maioria dos casos, é suficiente configurar corretamente os timeouts de acordo com o tipo de proxy, adicionar lógica de retry e implementar rotação em caso de falhas. Para tarefas com altos requisitos de estabilidade, use proxies residenciais com rotação automática — mais detalhes em proxycove.com.