Se você está construindo um scraper em Go com Colly e está sendo bloqueado após as primeiras 200 requisições, você não está sozinho. A rotação de proxies em Colly é a técnica que separa um scraper de prova de conceito de um sistema de coleta de dados em produção. Neste guia, vamos cobrir o modelo de collector do Colly, o proxy switcher nativo, integração com proxies residenciais geo-targeted, e padrões de produção para alto throughput.
Aviso legal: Este guia cobre técnicas para coleta de dados públicos. A raspagem de dados pode estar sujeita ao CFAA nos EUA, ao GDPR na UE, e aos Termos de Serviço de cada site. Respeite sempre o
robots.txt, os limites de taxa, e não colete dados pessoais sem base legal. Consulte um advogado para seu caso específico.
Entendendo a Rotação de Proxies em Colly: O Modelo de Collector
Colly é o framework de scraping mais popular para Go, construído sobre o goquery para traversal de DOM e o net/http padrão para transporte HTTP. Antes de mergulhar em proxies, é essencial entender o modelo de collector.
Um Collector no Colly é a unidade central que orquestra todo o ciclo de scraping. Ele expõe três callbacks principais:
- OnRequest(func(*http.Request)) — executado antes de cada requisição. Ideal para modificar headers, injetar proxies, ou logar URLs.
- OnHTML(gohtml.Callback) — executado quando o seletor CSS corresponde a um elemento. Aqui você extrai dados e descobre novos links.
- OnError(func(*http.Response, error)) — executado quando uma requisição falha. É onde você implementa retries.
O Colly também suporta modo assíncrono via c.Async = true, que usa goroutines internas para processar requisições concorrentemente. Quando combinado com c.Limit(), você controla o paralelismo e o delay entre requisições — fundamental para não sobrecarregar o alvo nem seus proxies.
Por que a rotação de proxies é necessária
Sites modernos usam sistemas anti-bot como Cloudflare, PerimeterX e DataDome que aplicam rate limits por IP. Um IP que faz 100 requisições em 10 segundos é flagged quase instantaneamente. A rotação distribui o tráfego entre múltiplos IPs, reduzindo a taxa por IP para níveis aceitáveis.
O problema é que proxies datacenter (AWS, DigitalOcean, Linode) são facilmente identificados por suas faixas de ASN. Sistemas anti-bot mantêm listas de ASNs datacenter e bloqueiam ou desafiam esses IPs automaticamente. É aqui que proxies residenciais entram — eles usam IPs de ISPs reais (Comcast, Deutsche Telekom, Vodafone), tornando o tráfego indistinguível de um usuário humano.
O Proxy Switcher Nativo do Colly
Colly oferece uma interface idiomática para rotação de proxies através do pacote colly/proxy. A função proxy.RoundRobinProxySwitcher aceita uma lista de URLs de proxy e retorna uma colly.ProxyFunc que alterna entre eles em round-robin a cada requisição.
A assinatura é:
// ProxyFunc é o tipo de função que Colly usa para determinar o proxy de cada requisição
type ProxyFunc func(*http.Request) (*url.URL, error)
// RoundRobinProxySwitcher cria um switcher round-robin a partir de uma lista de URLs
func RoundRobinProxySwitcher(ProxyURLs ...string) (ProxyFunc, error)
O switcher suporta os esquemas http, https e socks5 nativamente, já que Colly usa o net/http padrão de Go, que delega proxies SOCKS5 para o pacote golang.org/x/net/proxy.
Exemplo básico: RoundRobin com ProxyHat
package main
import (
"fmt"
"log"
"github.com/gocolly/colly"
"github.com/gocolly/colly/proxy"
)
func main() {
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"),
)
// Lista de proxies ProxyHat com países diferentes para rotação
proxyURLs := []string{
"http://user-country-US:pass@gate.proxyhat.com:8080",
"http://user-country-DE:pass@gate.proxyhat.com:8080",
"http://user-country-GB:pass@gate.proxyhat.com:8080",
"http://user-country-FR:pass@gate.proxyhat.com:8080",
}
rp, err := proxy.RoundRobinProxySwitcher(proxyURLs...)
if err != nil {
log.Fatal(err)
}
c.SetProxyFunc(rp)
c.OnHTML("title", func(e *colly.HTMLElement) {
fmt.Printf("Título: %s\n", e.Text)
})
c.OnRequest(func(r *colly.Request) {
fmt.Printf("Visitando %s\n", r.URL.String())
})
c.Visit("https://httpbin.org/ip")
}
Este exemplo funciona, mas tem uma limitação: a lista de proxies é fixa. Em produção, você quer gerar URLs de proxy dinamicamente — com sessões diferentes, cidades diferentes, e até rotação de credenciais. É aí que um proxy switcher customizado se torna necessário.
Proxy Switcher Customizado: Rotação Dinâmica com Sessões Residenciais
A verdadeira potência do Colly vem da possibilidade de passar uma função customizada para c.SetProxyFunc(). Em vez de uma lista estática, você pode gerar um URL de proxy único por requisição, controlando país, cidade e sessão.
O ProxyHat permite geo-targeting e controle de sessão através do username:
user-country-DE— IP residencial na Alemanhauser-country-DE-city-berlin— IP residencial em Berlimuser-session-abc123— sessão sticky (mesmo IP para todas as requisições com essa session ID)user-country-US-session-sess42— combinação de país e sessão
Switcher customizado com sessões por requisição
package main
import (
"fmt"
"math/rand"
"net/http"
"net/url"
"sync/atomic"
"time"
"github.com/gocolly/colly"
)
var (
proxyUser = "user"
proxyPass = "pass"
reqCount uint64
// Países para rotação geo-targeted
countries = []string{"US", "DE", "GB", "FR", "NL", "ES", "IT", "SE"}
)
func residentialProxyFunc(r *http.Request) (*url.URL, error) {
n := atomic.AddUint64(&reqCount, 1)
// Gera uma session ID única por requisição para IP novo a cada call
sessionID := fmt.Sprintf("sess-%d-%d", n, rand.Intn(100000))
// Escolhe um país aleatoriamente
country := countries[rand.Intn(len(countries))]
// Constrói o URL do proxy com geo-targeting e sessão
proxyURLStr := fmt.Sprintf("http://%s-country-%s-session-%s:%s@gate.proxyhat.com:8080",
proxyUser, country, sessionID, proxyPass)
return url.Parse(proxyURLStr)
}
func main() {
rand.Seed(time.Now().UnixNano())
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"),
)
// Define o proxy switcher customizado
c.SetProxyFunc(residentialProxyFunc)
// Rate limiting: máximo 5 requisições paralelas, delay aleatório de 1-3s
c.Limit(&colly.LimitRule{
DomainGlob: "*",
Parallelism: 5,
Delay: 2 * time.Second,
RandomDelay: 1 * time.Second,
})
c.OnHTML("body", func(e *colly.HTMLElement) {
fmt.Printf("[%s] IP detectado: %s\n", e.Request.URL, e.Text)
})
c.OnRequest(func(r *colly.Request) {
fmt.Printf("Requisição #%d -> %s\n", atomic.LoadUint64(&reqCount), r.URL.String())
})
c.OnError(func(r *colly.Response, err error) {
fmt.Printf("Erro em %s: %v (status %d)\n", r.Request.URL, err, r.StatusCode)
})
// Faz 20 requisições para verificar IPs diferentes
for i := 0; i < 20; i++ {
c.Visit("https://httpbin.org/ip")
}
c.Wait() // Aguarda todas as requisições assíncronas terminarem
}
Este exemplo é o coração de um scraper de produção. Cada requisição recebe um IP residencial diferente, com um país aleatório e uma sessão única. O LimitRule garante que no máximo 5 requisições rodem em paralelo, com um delay de 2s ± 1s aleatório entre elas.
Proxies Residenciais vs Datacenter vs Mobile: Qual Escolher
A escolha do tipo de proxy afeta diretamente a taxa de sucesso do seu scraper. Aqui está uma comparação prática:
| Característica | Residencial | Datacenter | Mobile |
|---|---|---|---|
| Taxa de sucesso (alvos hard) | 90-98% | 30-60% | 95-99% |
| Latência típica | 200-800ms | 50-150ms | 500-2000ms |
| Custo por GB | Médio | Baixo | Alto |
| Detectabilidade | Baixa (ISP real) | Alta (ASN datacenter) | Muito baixa (operadora) |
| Ideal para | Scraping geral, SERP, e-commerce | Alvos sem anti-bot, alto volume | Apps mobile, social media |
Para a maioria dos casos de web scraping com Colly, proxies residenciais oferecem o melhor equilíbrio entre custo, taxa de sucesso e velocidade. Confira os locais disponíveis no ProxyHat para planejar sua estratégia de geo-targeting.
Padrões de Produção: Retries, Transport Customizado e Escala
Retries com c.Clone()
Quando uma requisição falha (status 429, 503, ou timeout), o callback OnError é disparado. O padrão idiomático em Colly é usar c.Clone() para criar um collector filho com a mesma configuração, mas com um proxy diferente, e re-enfileirar a URL.
var retryCount = make(map[string]int)
var mu sync.Mutex
func setupRetries(c *colly.Collector) {
maxRetries := 3
c.OnError(func(r *colly.Response, err error) {
urlStr := r.Request.URL.String()
mu.Lock()
retryCount[urlStr]++
count := retryCount[urlStr]
mu.Unlock()
if count > maxRetries {
log.Printf("Max retries atingido para %s: %v\n", urlStr, err)
return
}
log.Printf("Retry %d/%d para %s (status %d)\n", count, maxRetries, urlStr, r.StatusCode)
// Clona o collector — herda OnHTML, OnRequest, proxy func, etc.
retryCollector := r.Crawler.Clone()
// Aguarda backoff exponencial antes de tentar novamente
backoff := time.Duration(count*count) * time.Second
time.Sleep(backoff)
retryCollector.Visit(urlStr)
})
}
O c.Clone() preserva todos os callbacks, configurações de proxy, e regras de Limit. O backoff exponencial (1s, 4s, 9s) evita sobrecarregar o servidor e dá tempo para o rate limit resetar.
Transport customizado com configuração TLS
Por padrão, Colly usa o http.DefaultTransport. Em produção, você pode querer customizar timeouts, pool de conexões, e configuração TLS para evitar erros de handshake em proxies com TLS interception:
func customTransport() http.RoundTripper {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 15 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false, // Mantenha false em produção
MinVersion: tls.VersionTLS12,
},
DisableKeepAlives: false,
}
}
// No collector:
c := colly.NewCollector()
c.SetTransport(customTransport())
O MaxIdleConnsPerHost: 10 é importante quando se usa um único gateway de proxy (gate.proxyhat.com) — sem isso, o Go pode esgotar o pool de conexões e criar overhead desnecessário de TLS handshakes.
Escala distribuída com Redis Storage
Para scraping distribuído em múltiplas máquinas, Colly oferece backends de storage via a interface storage.Storage. O pacote storage inclui implementações para Redis e SQLite.
import (
"github.com/gocolly/colly/storage"
"github.com/gocolly/redisstorage"
)
func setupDistributedCollector() *colly.Collector {
c := colly.NewCollector()
// Conecta ao Redis para deduplicação de URLs distribuída
storage := &redisstorage.Storage{
Address: "redis-cluster.internal:6379",
Password: "",
DB: 0,
Prefix: "colly_scraper_",
}
if err := c.SetStorage(storage); err != nil {
log.Fatal(err)
}
// O collector agora deduplica URLs via Redis,
// permitindo múltiplas instâncias sem visitar a mesma URL duas vezes
return c
}
Com Redis storage, múltiplas instâncias do seu scraper podem rodar em containers Docker separados, compartilhando a fila de URLs visitadas. Isso é essencial para SERP tracking em escala, onde você monitora milhares de keywords diariamente.
Containerização e headless fleet
Para escalar horizontalmente, empacote seu scraper Colly em um container Docker mínimo (imagem scratch ou alpine com binário Go estático) e orquestre com Kubernetes ou Docker Compose. Cada pod/container executa uma instância independente do collector, todas apontando para o mesmo Redis para deduplicação.
Um setup típico para alto throughput:
- 10-20 containers com 5 threads cada = 50-100 requisições concorrentes
- Redis cluster para deduplicação e fila de URLs
- ProxyHat residential pool com rotação de país por container (cada container usa um país fixo, reduzindo fingerprinting cross-country)
- RandomDelay de 2-5s entre requisições para simular comportamento humano
Erros Comuns e Armadilhas
1. Esquecer c.Wait() em modo assíncrono
Quando c.Async = true, c.Visit() retorna imediatamente sem esperar a resposta. Se o processo principal terminar antes, você perde dados. Sempre chame c.Wait() no final.
2. Usar proxy datacenter para alvos com Cloudflare
Cloudflare identifica ASNs datacenter com precisão superior a 99%. Se o alvo usa Cloudflare, proxies datacenter resultarão em desafios CAPTCHA ou bloqueios diretos. Use residenciais.
3. Não respeitar rate limits do proxy
O ProxyHat tem limites de concorrência por plano. Se seu plano suporta 100 conexões concorrentes e você abre 500, metade das requisições vai falhar com timeout. Configure Parallelism no LimitRule para ficar dentro do seu plano. Confira os planos do ProxyHat para detalhes.
4. Sessões sticky por tempo demais
Sessões sticky mantêm o mesmo IP, mas IPs residenciais podem mudar naturalmente (o usuário real pode desconectar). Se uma sessão durar mais de 30 minutos, o IP pode mudar inesperadamente. Renove session IDs periodicamente.
5. Ignorar o Content-Type
Colly só dispara OnHTML para respostas com Content-Type: text/html. Se o servidor retorna application/json ou text/plain, use OnResponse em vez de OnHTML.
Quando NÃO Usar Colly
Colly é excelente para scraping de HTML estático e APIs JSON. Mas não é a ferramenta certa para tudo:
- SPAs (Single Page Applications): Sites que renderizam conteúdo via JavaScript (React, Vue, Angular) não retornam HTML parseable no primeiro request. Colly não executa JS. Para esses casos, use chromedp ou Playwright para Go.
- Sites com proteção anti-bot agressiva: Se o alvo usa DataDome ou PerimeterX com challenges JS complexos, mesmo proxies residenciais podem não ser suficientes sem um browser headless. Considere combinar Colly com um browser headless para os requests que exigem JS.
- Streaming de dados: Colly não é projetado para WebSocket ou Server-Sent Events. Use
gorilla/websocketou o pacotenhooyr.io/websocket.
A estratégia híbrida mais comum em produção: usar Colly para 90% das URLs (HTML estático) e um fleet de browsers headless (chromedp + ProxyHat SOCKS5 em gate.proxyhat.com:1080) para os 10% que exigem renderização JS.
SOCKS5 em Colly: Quando e Como
Para casos que exigem maior compatibilidade ou tunneling de tráfego não-HTTP, o ProxyHat também oferece SOCKS5 na porta 1080. Colly suporta SOCKS5 nativamente através do golang.org/x/net/proxy:
// Exemplo de switcher SOCKS5 para ProxyHat
func socks5ProxyFunc(r *http.Request) (*url.URL, error) {
sessionID := fmt.Sprintf("sock-%d", time.Now().UnixNano())
proxyURLStr := fmt.Sprintf("socks5://%s-country-US-session-%s:%s@gate.proxyhat.com:1080",
proxyUser, sessionID, proxyPass)
return url.Parse(proxyURLStr)
}
c := colly.NewCollector()
c.SetProxyFunc(socks5ProxyFunc)
SOCKS5 é útil quando o tráfego precisa atravessar redes que filtram HTTP proxies, ou quando você precisa de tunneling para protocolos além de HTTP/HTTPS.
Considerações Éticas e Legais
Antes de deployar qualquer scraper, considere:
- Respeite
robots.txt: Colly não verificarobots.txtautomaticamente. Implemente uma verificação manual ou use a bibliotecatemoto/robotstxt. - Rate limiting responsável: Mesmo com rotação de proxies, não faça centenas de requisições por segundo a um único domínio. Isso pode impactar a infraestrutura do alvo.
- Dados pessoais e GDPR: Se você coleta dados pessoais de cidadãos da UE, precisa de base legal (consentimento, interesse legítimo). Consulte a documentação oficial do GDPR.
- Termos de Serviço: Violar ToS pode resultar em ação legal. Alguns ToS proíbem explicitamente scraping automatizado.
- Dados públicos apenas: Não contorne paywalls, autenticação, ou access controls. O CFAA nos EUA criminaliza acesso não-autorizado a sistemas de computação.
Para mais detalhes técnicos sobre a configuração de proxies no ProxyHat, consulte a documentação oficial do ProxyHat.
Key Takeaways
- Colly oferece proxy switchers nativos: Use
proxy.RoundRobinProxySwitcherpara listas estáticas ouc.SetProxyFunc(func)para rotação dinâmica com geo-targeting e sessões.- Proxies residenciais são essenciais para alvos hard: IPs de ISPs reais evitam detecção por ASN. Use
-country-XX-city-YYno username para geo-targeting preciso.- Sessões sticky vs rotação per-request: Use
-session-abc123quando precisar manter cookies/IP consistente; omita para um IP novo a cada requisição.- Rate limiting é obrigatório:
c.Limit()comParallelismeRandomDelaysimula comportamento humano e evita bans.- Retries via
c.Clone(): O padrão idiomático para retries preserva configuração e troca de IP automaticamente.- Escala com Redis storage: Para scraping distribuído, use
redisstorage.Storagepara deduplicação de URLs entre instâncias.- Colly não executa JS: Para SPAs, combine Colly com chromedp usando SOCKS5 em
gate.proxyhat.com:1080.
FAQ
O que é rotação de proxies em Colly?
Rotação de proxies em Colly é a prática de alternar endereços IP entre requisições HTTP usando o proxy switcher nativo do framework (proxy.RoundRobinProxySwitcher) ou uma função customizada via c.SetProxyFunc. Isso distribui o tráfego entre múltiplos IPs, evitando rate limits e bloqueios baseados em IP.
Por que a rotação de proxies em Colly importa para usuários de proxy?
Sites modernos impõem limites de taxa por IP (frequentemente 50-200 requisições por minuto) e bloqueiam IPs que excedem esses limites. Sem rotação, um scraper em Go pode ser banido em minutos. A rotação distribui requisições entre IPs residenciais, mantendo uma taxa de sucesso acima de 95% em alvos difíceis.
Qual tipo de proxy funciona melhor para rotação em Colly?
Proxies residenciais são os mais eficazes para rotação em Colly, pois usam IPs de ISPs reais que são difíceis de distinguir de tráfego humano. Proxies datacenter são mais rápidos, mas são facilmente detectados por sistemas anti-bot. Para alvos com proteção avançada (Cloudflare, PerimeterX), proxies residenciais com sessões sticky por cidade oferecem o melhor equilíbrio.
Como evitar bloqueios ao implementar rotação de proxies em Colly?
Combine rotação de IPs residenciais com delays aleatórios (RandomDelay entre 1-3s), limite de paralelismo adequado (c.Limit com 5-10 threads), headers realistas (User-Agent, Accept-Language), retries via c.Clone() em erros 429/503, e respeite robots.txt. Use sessões sticky quando o alvo exigir cookies de sessão consistentes.






