Resposta rápida
Quantos pedidos concorrentes posso rodar por proxies residenciais?
Tantos quanto cada site alvo tolerar: **CrawlRelay cobra o tráfego residencial por GB (US$ 0,50/GB a partir de 50 GB), não por thread ou conexão. Dimensione a concorrência com a lei de Little, pedidos em voo = páginas por segundo × segundos por página, depois limite por host, começando com 2 a 4. Diminua quando respostas 429 ou 503 aparecerem. Pago com cripto, sem KYC.
Concorrência em resumo
| Cobrança | Por GB de tráfego; threads e conexões não são cobrados |
|---|---|
| Regra de dimensionamento | Em voo = taxa de transferência × latência (lei de Little) |
| Ponto inicial por host | 2–4 pedidos paralelos; 1 com atraso para sites pequenos |
| Sinais para desacelerar | 429, 503, aumento nos tempos de resposta, páginas de desafio |
| Cálculo de largura de banda | Páginas por segundo × bytes por página × 3.600 = bytes por hora |
| Resposta mediana residencial | 0,93 s na rede de proxy, 3,21 s em alvos reais (Proxyway 2026) |
Como dimensiono a concorrência a partir da taxa de transferência que preciso?
Use a lei de Little: o número de pedidos em voo é igual à taxa desejada multiplicada pelo tempo de cada pedido. O Proxy Market Research 2026 do Proxyway mediu uma mediana de 3,21 segundos por pedido em alvos reais via proxies residenciais, então planeje com 2 a 4 segundos para sites reais, não apenas o dado abaixo de um segundo da rede.
| Meta | Tempo por pedido | Pedidos em voo |
|---|---|---|
| 5 páginas por segundo | 2 s | 10 |
| 20 páginas por segundo | 2 s | 40 |
| 20 páginas por segundo | 4 s | 80 |
| 100 páginas por segundo | 3 s | 300 |
Depois distribua esses pedidos entre hosts. Quarenta pedidos em voo distribuídos em 200 sites é tranquilo; quarenta em um site pequeno é um teste de carga que ele nunca concordou.
Quanta largura de banda essa taxa de transferência usa?
Multiplique páginas por segundo por bytes por página. Com 20 páginas HTML por segundo e 50 KB por página, o crawl move 1 MB por segundo, que é 3,6 GB por hora e cerca de 86 GB por dia. Com 2 MB por página renderizada, a mesma taxa moveria 40 MB por segundo.
Multiplique o resultado pelo preço do seu plano por GB, US$ 0,50/GB a partir de 50 GB a CrawlRelay, para obter o custo por hora. O guia do calculador de largura de banda mostra pesos típicos por tipo de página.
Quais limites por host devo começar?
| Alvo | Pedidos paralelos por host | Intervalo entre pedidos | Notas |
|---|---|---|---|
| Site pequeno (blog, loja local) | 1 | 2–5 s | Respeite Crawl-delay em robots.txt se presente |
| Site médio | 2–4 | 0,5–2 s | Observe os tempos de resposta; diminua o ritmo quando eles dobrarem |
| Plataforma grande com CDN | 4–16 | AutoThrottle ou adaptativo | Aumente apenas enquanto as taxas de 429 e 5xx permanecerem estáveis |
| API pública | O limite documentado | Conforme documentado | Use a chave da API e os limites fornecidos pelo provedor |
Como adapto a concorrência automaticamente?
Use aumento aditivo e diminuição multiplicativa, a regra TCP para congestão: adicione um slot após uma sequência de respostas limpas, reduza o limite pela metade em um 429 ou 503. O limitador abaixo mantém um limite por host, pausa cada requisição para esse host pelo atraso de Retry-After, tenta uma vez novamente, e nunca ultrapassa um teto que você configurar. Erros de rede retornam valores, então um timeout não interrompe o lote.
import asyncio
import random
from urllib.parse import urlsplit
import httpx
PROXY = "http://USERNAME-country-us:[email protected]:7777"
class AdaptiveLimit:
"""One per host: +1 slot after 20 clean responses, halve on 429/503, pause for Retry-After."""
def __init__(self, start=2, low=1, high=16):
self.limit, self.low, self.high = start, low, high
self.in_flight, self.clean = 0, 0
self.pause_until = 0.0
self.cond = asyncio.Condition()
async def __aenter__(self):
loop = asyncio.get_running_loop()
while True:
async with self.cond:
await self.cond.wait_for(lambda: self.in_flight < self.limit)
delay = self.pause_until - loop.time()
if delay <= 0:
self.in_flight += 1
return self
await asyncio.sleep(delay) # the host asked for a pause: wait outside the lock
async def __aexit__(self, *exc):
async with self.cond:
self.in_flight -= 1
self.cond.notify_all()
def record(self, status, retry_after=None):
if status in (429, 503):
self.limit, self.clean = max(self.low, self.limit // 2), 0
wait = int(retry_after) if retry_after and retry_after.isdigit() else 5 + random.random()
now = asyncio.get_running_loop().time()
self.pause_until = max(self.pause_until, now + wait)
elif status < 400:
self.clean += 1
if self.clean >= 20 and self.limit < self.high:
self.limit, self.clean = self.limit + 1, 0
gates = {}
def gate_for(url):
host = urlsplit(url).hostname
if host not in gates:
gates[host] = AdaptiveLimit()
return gates[host]
async def fetch(client, url, attempts=2):
gate = gate_for(url)
status = None
for _ in range(attempts):
try:
async with gate:
r = await client.get(url)
except httpx.HTTPError as exc:
return "error: %s" % type(exc).__name__
status = r.status_code
gate.record(status, r.headers.get("Retry-After"))
if status not in (429, 503):
break # the retry, if any, waits for the host's pause in __aenter__
return status
async def main(urls):
timeout = httpx.Timeout(30.0, connect=10.0)
async with httpx.AsyncClient(proxy=PROXY, timeout=timeout) as client:
print(await asyncio.gather(*(fetch(client, u) for u in urls)))
print({host: gate.limit for host, gate in gates.items()})
asyncio.run(main(["https://example.com/"] * 10))O que desacelera um crawl com proxy além do alvo?
- Handshake TLS. Uma conexão nova por requisição, necessária para um IP novo a cada vez, adiciona um handshake com o site toda vez. Mantenha uma sessão fixa com keep-alive para páginas de um site quando a diversidade de IP não importar.
- Exits lentos. Proxies residenciais e proxies móveis 4G são conexões reais residenciais e celulares. Use timeouts e tente uma nova conexão em vez de esperar.
- Alvo estreito. Uma cidade pequena ou um ASN único têm menos exits que um país. Foque apenas onde os dados mudam.
- Renderização. Um navegador headless consome muito mais CPU, memória e tráfego por página que um cliente HTTP. Renderize apenas as páginas que precisam de JavaScript.
- DNS. Com
socks5h://ou a porta HTTP, o DNS é resolvido no exit; não precisa de cache local para resolvers dos hosts alvo.
Perguntas sobre concorrência
CrawlRelay limita o número de threads ou conexões?
Os planos são cobrados por GB de tráfego, não por thread, conexão ou requisição, e os planos publicados não definem limite de threads. Os limites práticos são a tolerância de cada alvo e sua própria máquina. Para um crawl muito grande, escreva para [email protected] primeiro para verificarmos a cobertura do seu alvo.
Por que meu crawl ficou mais lento ao adicionar mais threads?
Normalmente porque o alvo começou a te limitar: os tempos de resposta aumentam, aparecem respostas 429 ou 503, e as tentativas aumentam a carga. Outra causa comum é sua própria máquina sem CPU ou sockets disponíveis, especialmente com navegadores headless. Meça a latência e taxas de erro por host antes de adicionar threads.
Uma conexão por requisição é mais lenta que keep-alive?
Sim, porque há um handshake TCP e TLS por requisição, tipicamente algumas centenas de milissegundos via proxy residencial. É o custo de um IP novo em cada requisição. Para muitas páginas de um site onde o IP não importa, uma sessão fixa com keep-alive é mais rápida e consome menos tráfego.
Como respeitar crawl-delay do robots.txt em crawlers concorrentes?
Parseie o robots.txt uma vez por host, faça cache até 24 horas conforme sugere o RFC 9309, e transforme um valor Crawl-delay no intervalo mínimo por host entre requisições com concorrência 1. O urllib.robotparser do Python expõe isso via crawl_delay().
Escalar com preços por GB
US$ 0,50/GB a partir de 50 GB até 1 TB, tráfego que nunca expira.