Coletar milhares de imagens para treinar um modelo de visão computacional é uma tarefa que quase sempre enfrenta dois problemas: bloqueios por parte da fonte de dados (Google Imagens, Pinterest, marketplaces, sites de estoque) e um consumo de tráfego inesperadamente alto. Um IP rapidamente é bloqueado, e a conta de tráfego de proxies pode superar o custo da própria rotulagem do conjunto de dados. Neste artigo, vamos discutir como projetar um parser de imagens que não perca dados devido a bloqueios e como calcular o orçamento para o tráfego antecipadamente.
Por que usar proxies ao coletar imagens para CV
Conjuntos de dados para visão computacional raramente são coletados de uma única fonte. Normalmente, é uma combinação de resultados de busca de imagens, páginas de produtos em marketplaces, bancos de imagens, redes sociais e APIs abertas. Quase todas essas fontes impõem restrições ao número de solicitações de um único IP: o Google Imagens exibe um captcha após algumas centenas de solicitações consecutivas, o Pinterest bloqueia temporariamente o acesso à API, e o Wildberries e Ozon retornam páginas vazias ou redirecionam para uma página de verificação em caso de atividade suspeita.
Sem proxies, você atinge um teto de algumas centenas ou milhares de imagens por dia de um único endereço. Se você precisa de um conjunto de dados com 50.000 a 500.000 imagens (e é isso que geralmente é necessário para redes convolucionais e transformadores em tarefas de classificação e detecção), sem um pool distribuído de endereços IP, coletá-lo em um tempo razoável é fisicamente impossível — você ou estará constantemente enfrentando bloqueios, ou obterá uma amostra muito distorcida devido às limitações de horário e geolocalização.
A segunda razão é a diversidade geográfica dos dados. Se o modelo deve reconhecer objetos igualmente bem, independentemente da região de captura (por exemplo, sinais de trânsito em diferentes países ou produtos em marketplaces locais), o parser deve se conectar com IPs de diferentes países. Um único datacenter com um único IP de saída não proporcionará essa diversidade.
Quanto tráfego é realmente necessário para o conjunto de dados
O consumo de tráfego ao coletar imagens é frequentemente subestimado. Não se trata apenas do peso das próprias imagens — páginas HTML, pacotes JS, pré-visualizações, pixels de rastreamento e CSS também são carregados através de proxies, se o parser não estiver configurado especificamente para as URLs necessárias.
| Tipo de conteúdo | Peso médio | Tráfego para 10.000 unidades |
|---|---|---|
| Pré-visualização/thumbnail (150–300 px) | 15–40 KB | ~250 MB |
| Foto de resolução média (800–1200 px) | 80–200 KB | ~1,4 GB |
| Foto de alta resolução (2000+ px) | 400 KB – 1,5 MB | ~9,5 GB |
| Página HTML de produto (sem imagens) | 150–400 KB | ~2,7 GB |
Na prática, se você coleta 100.000 imagens de resolução média junto com as páginas de produtos (para extrair rótulos — categorias, caixas, atributos), o consumo total de tráfego geralmente fica na faixa de 25–45 GB. Se você precisa de imagens de alta resolução para tarefas de detecção de pequenos objetos — calcule de 80 a 150 GB para as mesmas 100.000 unidades. Esses números devem ser considerados ao escolher o plano de proxies antecipadamente, e não depois.
Datacenter, residencial e móvel: o que escolher
Para a coleta de imagens, três parâmetros são importantes: velocidade, custo do tráfego e resistência a bloqueios. Esses parâmetros variam bastante entre os diferentes tipos de proxies.
| Parâmetro | Datacenter | Residencial | Móvel |
|---|---|---|---|
| Velocidade | Alta | Média | Baixa-média |
| Resistência a bloqueios | Baixa | Alta | Muito alta |
| Custo do tráfego | Baixo | Médio-alto | Alto |
| Adequado para | APIs abertas, estoques sem proteção rigorosa | Google Imagens, Pinterest, marketplaces | Redes sociais com proteção anti-bot rigorosa |
Para a maioria das tarefas de coleta de imagens, o equilíbrio ideal é fornecido por proxies residenciais — os IPs parecem usuários comuns, portanto, os sistemas anti-bot de marketplaces e motores de busca de imagens acionam menos frequentemente o captcha. Se a fonte for um banco de imagens aberto ou uma API sem limites rigorosos, é mais barato usar proxies de datacenter — eles são mais rápidos e permitem transferir grandes volumes de tráfego a um custo menor por GB. E para coletar imagens de aplicativos móveis ou redes sociais com proteção anti-bot avançada, proxies móveis são mais adequados — eles usam IPs reais de operadoras de telecomunicações e quase nunca são bloqueados.
Parser básico com requests e rotação de proxies
Para começar, um simples script síncrono com requests e rotação de proxies a cada solicitação ou após um número definido de solicitações funcionará. Abaixo está um exemplo de download de imagens a partir de uma lista de URLs, verificando o tipo de conteúdo e limitando o tamanho do arquivo para não baixar acidentalmente um arquivo pesado de formato incorreto.
import requests
import itertools
import hashlib
import os
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
proxy_cycle = itertools.cycle(PROXIES)
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "image/avif,image/webp,image/*,*/*;q=0.8",
}
def download_image(url, save_dir="dataset", max_size_mb=10, timeout=15):
proxy = next(proxy_cycle)
proxies = {"http": proxy, "https": proxy}
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=timeout, stream=True)
resp.raise_for_status()
content_type = resp.headers.get("Content-Type", "")
if "image" not in content_type:
return None
content_length = int(resp.headers.get("Content-Length", 0))
if content_length > max_size_mb * 1024 * 1024:
return None
file_hash = hashlib.md5(url.encode()).hexdigest()
ext = content_type.split("/")[-1].split(";")[0]
filepath = os.path.join(save_dir, f"{file_hash}.{ext}")
os.makedirs(save_dir, exist_ok=True)
with open(filepath, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
return filepath
except requests.exceptions.RequestException as e:
print(f"Erro ao acessar {url} através de {proxy}: {e}")
return None
O ponto chave é verificar Content-Length antes de baixar o corpo da resposta. Isso economiza tráfego, filtrando arquivos muito pesados antes do download completo. A rotação de proxies através de itertools.cycle distribui uniformemente a carga entre os IPs, reduzindo o risco de bloqueio de um endereço específico.
Coleta assíncrona com aiohttp
Um script síncrono com requests é adequado para algumas milhares de imagens, mas para conjuntos de dados a partir de 50.000 arquivos, a assíncronia é necessária — caso contrário, a coleta se estenderá por dias. O aiohttp, junto com um semáforo para limitar o paralelismo, é uma solução viável para dezenas de milhares de tarefas simultâneas sem sobrecarregar o pool de proxies.
import asyncio
import aiohttp
import itertools
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
proxy_cycle = itertools.cycle(PROXIES)
sem = asyncio.Semaphore(50) # limite de solicitações paralelas
async def fetch_image(session, url, idx):
proxy = next(proxy_cycle)
async with sem:
try:
async with session.get(url, proxy=proxy, timeout=15) as resp:
if resp.status != 200:
return
content_type = resp.headers.get("Content-Type", "")
if "image" not in content_type:
return
data = await resp.read()
with open(f"dataset/{idx}.jpg", "wb") as f:
f.write(data)
except Exception as e:
print(f"Erro ao acessar {url}: {e}")
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_image(session, url, i) for i, url in enumerate(urls)]
await asyncio.gather(*tasks)
# asyncio.run(main(url_list))
Um semáforo de 50 conexões simultâneas é um ponto de partida. Se os proxies residenciais suportam a carga sem aumento nas taxas de erro 429 e timeouts, você pode aumentar gradualmente para 100–150. O principal é monitorar a porcentagem de solicitações malsucedidas: se ela crescer acima de 5–7%, é melhor reduzir o paralelismo, em vez de aumentar o pool de proxies indefinidamente.
Scrapy + middleware de proxies para coleta em larga escala
Para a coleta em produção de conjuntos de dados com fila embutida, tentativas e deduplicação de URLs, é mais conveniente usar o Scrapy com ImagesPipeline. Ele mesmo baixa as imagens, verifica sua validade através do PIL e pode gerar pré-visualizações de diferentes tamanhos.
# settings.py
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline': 1,
}
IMAGES_STORE = 'dataset_images'
IMAGES_MIN_HEIGHT = 200
IMAGES_MIN_WIDTH = 200
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.ProxyRotationMiddleware': 350,
}
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 0.5
RETRY_TIMES = 3
# middlewares.py
import random
class ProxyRotationMiddleware:
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
def process_request(self, request, spider):
request.meta['proxy'] = random.choice(self.PROXIES)
Os parâmetros IMAGES_MIN_HEIGHT e IMAGES_MIN_WIDTH automaticamente descartam ícones pequenos e sprites, que poluem o conjunto de dados e consomem tráfego desnecessariamente. DOWNLOAD_DELAY de 0,5 segundos reduz a frequência de solicitações de um único proxy, diminuindo a incidência de captchas em fontes com monitoramento anti-fraude rigoroso, como o Pinterest.
Como reduzir o consumo de tráfego
O tráfego através de proxies geralmente é tarifado por volume, portanto, qualquer otimização economiza diretamente o orçamento. Aqui estão algumas técnicas eficazes:
- Solicite pré-visualizações, não o original, se a resolução de 512–1024 px for suficiente para o treinamento — a maioria das fontes fornece a imagem do tamanho necessário em uma URL separada ou parâmetro de consulta.
- Filtre pelo Content-Length antes de baixar o corpo — uma solicitação HEAD ou leitura dos cabeçalhos de um GET em streaming permite descartar arquivos muito pesados sem consumir tráfego para baixá-los.
- Desative o carregamento de recursos adicionais, se o parser estiver operando através de um navegador headless: bloqueie CSS, fontes e scripts de rastreamento, deixando apenas HTML e as imagens necessárias.
- Deduplica URLs antes da solicitação, e não após o download — imagens idênticas em diferentes páginas de produtos (por exemplo, no Wildberries) não precisam ser baixadas novamente.
- Use um cache local de respostas durante execuções repetidas do parser durante a depuração, para não consumir tráfego através de proxies em cada execução de teste do código.
Erros comuns, 403 e limite de taxa
Ao coletar imagens em grandes volumes, o parser inevitavelmente encontrará respostas 403, 429 e redirecionamentos temporários para páginas de verificação. O tratamento correto desses códigos economiza tanto tráfego quanto o pool de proxies.
| Código/sintoma | Causa | O que fazer |
|---|---|---|
| 403 Forbidden | IP na lista negra da fonte | Rotação de proxies, mudança de User-Agent |
| 429 Too Many Requests | Limite de solicitações por minuto excedido | Aumentar DOWNLOAD_DELAY, reduzir paralelismo |
| Redirecionamento para captcha | Análise comportamental suspeitou de um bot | Mudar para IPs residenciais/móveis |
| Content-Length vazio | Resposta chunked ou placeholder em vez da imagem | Verificar o tamanho real dos bytes baixados |
É importante registrar a porcentagem de solicitações bem-sucedidas para cada proxy. Se um IP específico consistentemente retorna 403 com mais frequência do que os outros — remova-o da rotação automaticamente, sem esperar que ele afete a estatística geral de bloqueio em todo o pool.
Cálculo do orçamento para tráfego de proxies
Antes de iniciar a coleta, é útil estimar o volume de tráfego antecipadamente, em vez de calcular após o fato. A fórmula é simples: número de imagens × peso médio do arquivo × coeficiente de sobrecarga (HTML, cabeçalhos, solicitações repetidas devido a erros — geralmente 1,3–1,6).
Exemplo de cálculo para um conjunto de dados de 200.000 imagens de resolução média (~150 KB) com parsing de páginas de produtos: 200.000 × 150 KB = 30 GB de peso líquido das imagens. Considerando as páginas HTML e as solicitações repetidas devido a bloqueios temporários (coeficiente 1,5), o consumo total é de cerca de 45 GB. Se parte das fontes exigir IPs móveis devido à proteção anti-bot, considere isso no pool de proxies separadamente, pois o tráfego em IPs móveis geralmente é mais caro do que em residenciais ou de datacenter.
Uma boa prática é coletar um lote piloto de 1.000–2.000 imagens, medir o consumo real de tráfego e a porcentagem de erros, e então extrapolar para todo o volume planejado do conjunto de dados. Isso é mais preciso do que qualquer cálculo teórico, pois leva em conta a especificidade da fonte específica.
Conclusão
Coletar imagens para treinar modelos de visão computacional é uma tarefa que está na interseção da engenharia de dados e da gestão eficaz da infraestrutura de rede. A arquitetura correta do parser (assíncrona, filtragem por Content-Length, deduplicação de URLs) economiza tráfego, enquanto a escolha cuidadosa de proxies determina se você coletará o volume necessário do conjunto de dados em dias ou em semanas de bloqueios constantes.
Se as fontes de dados utilizam proteção anti-bot avançada — motores de busca de imagens, marketplaces, redes sociais — comece com proxies residenciais: eles oferecem o melhor equilíbrio entre resistência a bloqueios e custo de tráfego. Para APIs abertas e bancos de imagens sem limites rigorosos, vale a pena economizar em proxies de datacenter, e para os aplicativos móveis mais protegidos — conectar proxies móveis para a parte final, mais complexa da coleta.