Rotazione Proxy in Colly: Guida Pratica per Go Developer

Una guida completa all'integrazione di proxy rotanti in Colly, il framework di web scraping per Go. Copre RoundRobinProxySwitcher, SetProxyFunc, proxy residenziali, pattern di produzione e best practice etiche.

Rotating Proxies in Colly: A Framework-Idiomatic Guide for Go Scrapers
In questo articolo

Se stai costruendo uno scraper ad alto throughput in Go con Colly, prima o poi ti imbatterai in blocchi IP, rate limiting e sfide anti-bot. La rotazione proxy in Colly è la tecnica che permette al tuo collector di distribuire le richieste su più indirizzi IP, riducendo il rischio di ban e aumentando la percentuale di successo. In questa guida esploriamo come integrare proxy residenziali in modo idiomatico, usando proxy.RoundRobinProxySwitcher, SetProxyFunc e pattern di produzione per scalare a migliaia di richieste al minuto.

Nota legale ed etica: Questa guida si concentra su dati pubblici accessibili senza autenticazione. Rispetta sempre il file robots.txt, i termini di servizio dei siti target e le normative vigenti come il GDPR in Europa e il CFAA negli Stati Uniti. Lo scraping di dati personali senza consenso può violare la legge.

Perché la Rotazione Proxy in Colly è Necessaria

Quando invii richieste HTTP da un singolo IP, il server target può facilmente identificare il tuo traffico come automatizzato. Siti di e-commerce, motori di ricerca e piattaforme social utilizzano sistemi anti-bot che monitorano:

  • Volume di richieste per IP in una finestra temporale (es. oltre 100 richieste/minuto).
  • Pattern ripetitivi negli header User-Agent e Accept.
  • Comportamento di navigazione non umano (tempo tra le richieste, sequenza di URL).
  • Fingerprinting TLS/HTTP tramite librerie come net/http di Go.

La rotazione proxy risolve il primo problema: distribuendo le richieste su centinaia o migliaia di IP diversi, ogni singolo indirizzo vede un volume di traffico che appare normale. I proxy residenziali sono particolarmente efficaci perché utilizzano IP assegnati a veri ISP, rendendo il traffico indistinguibile da quello di un utente reale.

Il Modello Collector di Colly

Colly è costruito attorno al tipo colly.Collector, che gestisce il ciclo di vita di una sessione di scraping. Il collector espone tre callback fondamentali:

OnRequest, OnHTML e OnError

Il callback OnRequest viene invocato prima che ogni richiesta venga inviata. È qui che puoi modificare header, aggiungere cookie o, come vedremo, gestire la rotazione proxy a livello di singola richiesta. OnHTML riceve il documento HTML parsato tramite goquery, permettendo di attraversare il DOM con selettori CSS. OnError gestisce fallimenti HTTP, timeout e altri errori di rete.

c := colly.NewCollector()

c.OnRequest(func(r *colly.Request) {
    r.Headers.Set("User-Agent", "MyBot/1.0")
})

c.OnHTML("a[href]", func(e *colly.HTMLElement) {
    link := e.Attr("href")
    fmt.Println(link)
})

c.OnError(func(r *colly.Response, err error) {
    log.Printf("Errore su %s: %v", r.Request.URL, err)
})

Modalità Asincrona

Per default, Colly esegue le richieste in modo sincrono, bloccando fino al completamento. Impostando c.Async = true, il collector invia richieste in modo asincrono, gestendo la concorrenza internamente con un pool di goroutine. Questo è fondamentale per lo scraping ad alta velocità, ma richiede attenzione: le callback vengono eseguite in goroutine separate, quindi è necessario sincronizzare l'accesso a dati condivisi con sync.Mutex o canali.

La Superficie Proxy Idiomatica di Colly

Colly offre due approcci per la rotazione proxy, entrambi integrati nel framework in modo nativo.

proxy.RoundRobinProxySwitcher

Il pacchetto colly/proxy fornisce RoundRobinProxySwitcher, che crea una funzione di rotazione round-robin a partire da una lista di URL proxy. La funzione restituita viene passata a c.SetProxyFunc():

import (
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/proxy"
)

proxies := []string{
    "http://user-country-DE:pass@gate.proxyhat.com:8080",
    "http://user-country-US:pass@gate.proxyhat.com:8080",
    "http://user-country-GB:pass@gate.proxyhat.com:8080",
}

rp, err := proxy.RoundRobinProxySwitcher(proxies...)
if err != nil {
    log.Fatal(err)
}
c.SetProxyFunc(rp)

Questo approccio è semplice e funziona bene quando si ha una lista statica di endpoint proxy. Tuttavia, per scenari più avanzati — come la rotazione dinamica di parametri geo o sessioni — è preferibile un approccio custom.

SetProxyFunc con funzione custom

Colly permette di passare qualsiasi funzione con signature func(*http.Request) (*url.URL, error) a SetProxyFunc. Questo è il punto di estensione idiomatico per implementare logiche di rotazione personalizzate:

import (
    "math/rand"
    "net/http"
    "net/url"
)

func proxyHatSwitcher(r *http.Request) (*url.URL, error) {
    countries := []string{"DE", "US", "GB", "FR", "IT"}
    country := countries[rand.Intn(len(countries))]
    session := fmt.Sprintf("sess-%d", rand.Intn(10000))
    
    proxyURL := fmt.Sprintf(
        "http://user-country-%s-session-%s:pass@gate.proxyhat.com:8080",
        country, session,
    )
    return url.Parse(proxyURL)
}

c.SetProxyFunc(proxyHatSwitcher)

Con questo pattern, ogni richiesta HTTP passa attraverso la funzione custom, che può selezionare il proxy in base a qualsiasi criterio: URL target, stato della richiesta precedente, parametri dinamici nel username, o una lista di endpoint aggiornata a runtime.

Perché i Proxy Residenziali e Come Ruotare Geo e Sessioni

I proxy datacenter sono economici ma utilizzano range IP facilmente identificabili come hosting provider (AWS, DigitalOcean, ecc.). I siti con protezioni anti-bot avanzate bloccano sistematicamente questi range. I proxy residenziali, invece, utilizzano IP assegnati a ISP reali, rendendo il traffico indistinguibile da quello di un utente domestico.

ProxyHat supporta la rotazione di parametri geo e sessione direttamente nel campo username. Questo è particolarmente potente perché permette di controllare il comportamento del proxy senza dover gestire una pool di endpoint separati:

  • user-country-DE-city-berlin — geo-targeting a livello di paese e città.
  • user-session-abc123 — sessione sticky: tutte le richieste con la stessa sessione escono dallo stesso IP.
  • user-country-US-session-r2d2 — combinazione di geo-targeting e sessione sticky.

Per lo scraping SERP, dove Google e altri motori di ricerca servono risultati localizzati, il geo-targeting è essenziale. Per i flussi di autenticazione multi-step, le sessioni sticky garantiscono che il cookie di sessione rimanga valido.

Esempio Runnable: Collector con Proxy Residenziali

Ecco un esempio completo che combina rotazione proxy residenziale, limiti di concorrenza, delay random e gestione errori:

package main

import (
    "fmt"
    "log"
    "math/rand"
    "net/http"
    "net/url"
    "sync/atomic"
    "time"

    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/extensions"
)

var reqCounter uint64

func residentialSwitcher(r *http.Request) (*url.URL, error) {
    countries := []string{"DE", "US", "GB", "FR", "IT", "ES", "NL"}
    country := countries[rand.Intn(len(countries))]
    sessionID := atomic.AddUint64(&reqCounter, 1)
    session := fmt.Sprintf("colly-%d", sessionID)

    proxyStr := fmt.Sprintf(
        "http://user-country-%s-session-%s:YOUR_PASSWORD@gate.proxyhat.com:8080",
        country, session,
    )
    return url.Parse(proxyStr)
}

func main() {
    c := colly.NewCollector(
        colly.MaxDepth(2),
        colly.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)"),
    )

    // Modalità asincrona per alto throughput
    c.Async = true

    // Limite di concorrenza: 10 richieste parallele
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 10,
        Delay:       2 * time.Second,
        RandomDelay: 1500 * time.Millisecond,
    })

    // Rotazione proxy residenziale
    c.SetProxyFunc(residentialSwitcher)

    // Estensioni: header realistici
    extensions.RandomUserAgent(c)
    extensions.Referer(c)

    visited := make(map[string]bool)
    var mu sync.Mutex

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        link := e.Request.AbsoluteURL(e.Attr("href"))
        mu.Lock()
        if !visited[link] {
            visited[link] = true
            e.Request.Visit(link)
        }
        mu.Unlock()
    })

    c.OnResponse(func(r *colly.Response) {
        fmt.Printf("[%d] %s — %d bytes\n",
            r.StatusCode, r.Request.URL, len(r.Body))
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Printf("Errore %d su %s: %v",
            r.StatusCode, r.Request.URL, err)
        // Retry con clone
        if r.StatusCode == 429 || r.StatusCode == 503 {
            clone := r.Request.Ctx.GetAny("collector").(*colly.Collector)
            _ = clone.Visit(r.Request.URL.String())
        }
    })

    c.Visit("https://example.com")
    c.Wait() // Attende il completamento di tutte le richieste asincrone
}

In questo esempio, ogni richiesta genera un nuovo session ID e un paese casuale, garantendo che ogni richiesta esca da un IP diverso. Il LimitRule con Parallelism: 10 e un RandomDelay di 1500ms simula un comportamento più umano. Il metodo c.Wait() blocca fino al completamento di tutte le richieste asincrone.

Pattern di Produzione

Retry con c.Clone()

Per gestire fallimenti temporanei (429 Too Many Requests, 503 Service Unavailable), Colly permette di clonare il collector con c.Clone(). Il clone eredita tutte le impostazioni tranne le callback, permettendo di configurare una strategia di retry separata:

retryCollector := c.Clone()
retryCollector.OnError(func(r *colly.Response, err error) {
    log.Printf("Retry fallito per %s", r.Request.URL)
})

// Nel callback OnError del collector principale:
// retryCollector.Visit(r.Request.URL.String())

Configurazione TLS Custom

Per evitare fingerprinting TLS, puoi personalizzare il http.Transport del collector. Questo è particolarmente importante per siti che utilizzano JA3 fingerprinting:

c.WithTransport(&http.Transport{
    TLSClientConfig: &tls.Config{
        InsecureSkipVerify: false,
        MinVersion:         tls.VersionTLS12,
        CipherSuites: []uint16{
            tls.TLS_AES_128_GCM_SHA256,
            tls.TLS_AES_256_GCM_SHA384,
            tls.TLS_CHACHA20_POLY1305_SHA256,
        },
    },
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     30 * time.Second,
})

Scraping Distribuito con Storage Backend

Per scalare oltre un singolo processo, Colly supporta storage backend distribuiti. Usando il backend Redis, più istanze del collector possono condividere la lista degli URL visitati e coordinare il lavoro:

import "github.com/gocolly/redisstorage"

storage := &redisstorage.Storage{
    Address:  "redis://localhost:6379",
    Prefix:   "my_scraper",
    Type:     redisstorage.StorageTypeVisitedURLs,
}

err := c.SetStorage(storage)
if err != nil {
    log.Fatal(err)
}

Questo permette di eseguire il scraper in container multipli su Kubernetes o Docker Swarm, con ogni istanza che processa una porzione del lavoro senza duplicare le richieste. Per approfondire le opzioni di piani proxy e località disponibili, consulta le relative pagine.

Confronto: Residenziali vs Datacenter vs Mobile

Caratteristica Residenziali Datacenter Mobile
Origine IP ISP reali (domestici) Provider hosting (AWS, DO) Reti mobili (4G/5G)
Rilevabilità anti-bot Bassa Alta Minima
Costo per GB Medio Basso Alto
Latenza tipica 200-800ms 50-150ms 400-1200ms
Success rate su target hard 90-98% 40-60% 95-99%

Per la maggior parte dei casi d'uso di web scraping e SERP tracking, i proxy residenziali offrono il miglior compromesso tra costo e affidabilità. I proxy mobile sono superiori per target estremamente protetti ma hanno costi significativamente più alti.

Errori Comuni e Edge Cases

1. Non sincronizzare l'accesso ai dati in modalità async

Quando c.Async = true, le callback vengono eseguite in goroutine separate. L'accesso a mappe, slice o contatori senza sincronizzazione causa data race. Usa sempre sync.Mutex o sync/atomic.

2. Dimenticare c.Wait() in modalità async

Senza c.Wait(), il programma termina prima che le richieste asincrone vengano completate. Questo è l'errore più comune per chi passa da sync ad async.

3. Usare lo stesso session ID per troppe richieste

Le sessioni sticky mantengono lo stesso IP, ma se invii centinaia di richieste con la stessa sessione, il sito target vedrà comunque un volume sospetto da quell'IP. Ruota le sessioni ogni 50-100 richieste per target sensibili.

4. Ignorare il codice di stato 429

Il codice 429 (Too Many Requests) indica che il server sta rate-limitando il tuo IP. Se lo ignori e continui a inviare richieste, il ban diventa permanente. Implementa sempre un backoff esponenziale quando ricevi un 429.

5. Non rispettare robots.txt

Colly non controlla automaticamente robots.txt. Puoi abilitare il controllo con c.IgnoreRobotsTxt = false (il default è true, cioè ignora). Rispettare robots.txt non è solo etico, ma previene anche ban automatici da parte di sistemi come Google che monitorano la violazione.

Quando NON Usare Colly

Colly è eccellente per scraping di pagine HTML statiche o server-rendered. Tuttavia, per applicazioni single-page (SPA) che richiedono esecuzione JavaScript — come siti React, Vue o Angular — Colly non può eseguire il JS necessario per renderizzare il contenuto. In questi casi, considera:

  • Chromedp o Playwright for Go per un controllo completo del browser headless.
  • Rod come alternativa più semplice a Chromedp.
  • Un approccio ibrido: usa Colly per la discovery degli URL e un browser headless per il rendering delle pagine JS-heavy.

Per il pattern ibrido, puoi combinare Colly per la fase di crawling con un pool di browser headless gestiti tramite container Docker, dove ogni container esegue un'istanza Chromium con un proxy residenziale assegnato. Consulta la documentazione di ProxyHat per dettagli sull'integrazione.

Key Takeaways

  • RoundRobinProxySwitcher è la via più semplice per la rotazione, ma SetProxyFunc con funzione custom offre controllo totale su geo-targeting e sessioni.
  • I proxy residenziali sono essenziali per target con protezioni anti-bot avanzate: i proxy datacenter vengono bloccati nel 40-60% dei casi.
  • Ruota i parametri -country-XX e -session-XXX nel username ProxyHat per controllare geo e sticky session per-request.
  • In modalità async, sincronizza sempre l'accesso ai dati condivisi e chiama c.Wait() prima di terminare.
  • Implementa retry con c.Clone() e backoff esponenziale per gestire 429 e 503.
  • Per scraping distribuito, usa il backend Redis per condividere lo stato tra istanze multiple.
  • Colly non esegue JavaScript: per SPA, usa un browser headless o un approccio ibrido.
  • Rispetta sempre robots.txt, i termini di servizio e le normative GDPR/CFAA.

FAQ

Che cos'è la rotazione proxy in Colly?

La rotazione proxy in Colly è la pratica di distribuire le richieste HTTP su più indirizzi IP proxy, utilizzando proxy.RoundRobinProxySwitcher o una funzione custom passata a SetProxyFunc. Questo riduce il rischio di ban IP e permette di scalare lo scraping a migliaia di richieste senza superare i rate limit del target.

Perché la rotazione proxy in Colly è importante per gli scraper?

Senza rotazione proxy, tutte le richieste escono dallo stesso IP, rendendo lo scraper facilmente identificabile e bloccabile dai sistemi anti-bot. La rotazione distribuisce il traffico su centinaia di IP residenziali, riducendo il volume per-IP a livelli che appaiono normali e aumentando il success rate dal 40-60% al 90-98% sui target più protetti.

Quale tipo di proxy funziona meglio con Colly?

I proxy residenziali offrono il miglior compromesso per la maggior parte dei casi d'uso in Colly, perché utilizzano IP di ISP reali che non vengono bloccati dai sistemi anti-bot. I proxy datacenter sono più economici ma vengono rilevati e bloccati frequentemente. I proxy mobile hanno il success rate più alto (95-99%) ma costano significativamente di più. Per SERP scraping e e-commerce, i residenziali sono la scelta ottimale.

Come evitare i blocchi implementando la rotazione proxy in Colly?

Oltre alla rotazione proxy, implementa: RandomDelay di 1-3 secondi tra le richieste, Parallelism limitato a 5-20 connessioni, header User-Agent realistici tramite extensions.RandomUserAgent, retry con backoff esponenziale sui codici 429/503, e ruota i parametri -country e -session nel username del proxy. Rispetta sempre robots.txt e non superare i 100-200 richieste per sessione.

Domande frequenti

Che cos'è la rotazione proxy in Colly?

La rotazione proxy in Colly è la pratica di distribuire le richieste HTTP su più indirizzi IP proxy, utilizzando proxy.RoundRobinProxySwitcher o una funzione custom passata a SetProxyFunc. Questo riduce il rischio di ban IP e permette di scalare lo scraping a migliaia di richieste senza superare i rate limit del target.

Perché la rotazione proxy in Colly è importante per gli scraper?

Senza rotazione proxy, tutte le richieste escono dallo stesso IP, rendendo lo scraper facilmente identificabile e bloccabile dai sistemi anti-bot. La rotazione distribuisce il traffico su centinaia di IP residenziali, riducendo il volume per-IP a livelli che appaiono normali e aumentando il success rate dal 40-60% al 90-98% sui target più protetti.

Quale tipo di proxy funziona meglio con Colly?

I proxy residenziali offrono il miglior compromesso per la maggior parte dei casi d'uso in Colly, perché utilizzano IP di ISP reali che non vengono bloccati dai sistemi anti-bot. I proxy datacenter sono più economici ma vengono rilevati e bloccati frequentemente. I proxy mobile hanno il success rate più alto (95-99%) ma costano significativamente di più.

Come evitare i blocchi implementando la rotazione proxy in Colly?

Oltre alla rotazione proxy, implementa RandomDelay di 1-3 secondi tra le richieste, Parallelism limitato a 5-20 connessioni, header User-Agent realistici tramite extensions.RandomUserAgent, retry con backoff esponenziale sui codici 429/503, e ruota i parametri -country e -session nel username del proxy. Rispetta sempre robots.txt e non superare i 100-200 richieste per sessione.

Verifica la tua configurazione proxy in pochi secondi

Verificatore di proxy gratuito — conferma che i tuoi IP siano veloci, anonimi e non bloccati.

Controlla i proxy gratis
← Torna al Blog