Rotation de proxies dans Colly : Guide complet pour Go

Apprenez à intégrer des proxys rotatifs résidentiels dans Colly de manière idiomatique : proxy switcher, SetProxyFunc, géo-ciblage, retries, et patterns de production pour des scrapers Go haute performance.

Rotating Proxies in Colly: A Framework-Idiomatic Guide for Go Scrapers
Dans cet article

La rotation de proxies dans Colly est la compétence technique la plus critique pour tout développeur Go qui construit des scrapers à grande échelle. Colly — le framework de scraping le plus adopté en Go, avec plus de 24 000 étoiles sur GitHub — offre des points d'extension natifs pour intégrer des proxys rotatifs sans bidouille. Ce guide couvre l'architecture du Collector, les middlewares de proxy idiomatiques, les stratégies de rotation résidentielle avec géo-ciblage, et les patterns de production pour des scrapers fiables dépassant 100 requêtes par seconde.

Avant de plonger dans le code, une note importante : le scraping de données publiques doit respecter le robots.txt, les conditions d'utilisation des sites cibles, et les réglementations en vigueur comme le RGPD en Europe ou le CFAA aux États-Unis. Ce guide présente des techniques d'ingénierie, pas des conseils juridiques.

Pourquoi la rotation de proxies dans Colly est indispensable

Tout scraper Go qui envoie plus de 50 requêtes par minute vers un même domaine finit par rencontrer des blocages. Les systèmes anti-bot modernes — Cloudflare, Datadome, PerimeterX — analysent le comportement au niveau de l'IP : taux de requêtes, entêtes, patterns de navigation. Un seul IP qui envoie 200 requêtes en 60 secondes vers un site e-commerce déclenche quasi-systématiquement un CAPTCHA ou un blocage HTTP 403.

La colly proxy rotation résout ce problème en distribuant les requêtes across un pool d'adresses IP. Au lieu que toutes les requêtes proviennent d'une seule IP de datacenter, elles arrivent depuis des dizaines ou centaines d'IPs résidentielles distinctes, chacune avec un comportement qui ressemble à celui d'un utilisateur réel.

Le défi technique n'est pas juste d'avoir des proxys — c'est de les intégrer proprement dans le flux de requêtes de Colly, avec rotation par requête, gestion de sessions persistantes, retry sur échec, et respect des limites de taux. C'est exactement ce que ce guide couvre.

Le modèle Collector de Colly : callbacks, async et goquery

Colly repose sur un modèle d'événements centré sur le Collector. Un Collector est l'objet principal qui orchestre les requêtes HTTP, le parsing HTML, et la gestion de file d'attente. Comprendre son architecture est essentiel pour intégrer la rotation de proxys de manière idiomatique.

Callbacks principaux

Colly expose plusieurs hooks que vous surchargez pour intercepter le cycle de vie d'une requête :

  • OnRequest(func(*colly.Request)) — déclenché avant l'envoi. Idéal pour injecter des entêtes dynamiques, des User-Agents, ou des cookies.
  • OnHTML(selector string, func(*colly.HTMLElement)) — déclenché quand le sélecteur CSS correspond. Utilise goquery sous le capot pour traverser le DOM.
  • OnError(func(*colly.Response, error)) — déclenché sur erreur HTTP ou de parsing. Point d'ancrage pour la logique de retry.
  • OnResponse(func(*colly.Response)) — déclenché après réception de la réponse complète, avant le parsing HTML.
  • OnScraped(func(*colly.Response)) — déclenché après tous les callbacks OnHTML. Utile pour le post-traitement.

Mode asynchrone et parallélisme

Colly supporte un mode asynchrone via c.Async = true. En mode async, les requêtes sont envoyées en parallèle dans des goroutines internes. Vous devez appeler c.Wait() pour bloquer jusqu'à ce que toutes les requêtes en file d'attente soient terminées. Le parallélisme est contrôlé par c.WithTransport() et les règles Limit().

goquery et le parcours DOM

Sous le capot, Colly utilise goquery, un portage de l'API jQuery en Go. Le *colly.HTMLElement expose e.DOM qui est un *goquery.Selection. Vous pouvez chaîner Find(), Each(), Text(), Attr() exactement comme en JavaScript. Cette intégration est transparente — pas besoin d'importer goquery directement pour la plupart des cas d'usage.

La surface de proxy idiomatique dans Colly

Colly expose deux mécanismes pour injecter des proxys : le proxy.RoundRobinProxySwitcher intégré et la fonction SetProxyFunc personnalisée. Les deux sont conçus pour s'intégrer dans le pipeline de requêtes sans modifier la logique de scraping.

proxy.RoundRobinProxySwitcher

Le switcher round-robin est la solution la plus simple. Vous lui passez une liste d'URLs de proxy, et il les fait tourner séquentiellement à chaque requête :

package main

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

func main() {
    proxies := []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(proxies...)
    if err != nil {
        log.Fatal(err)
    }

    c := colly.NewCollector()
    c.SetProxyFunc(rp)

    c.OnHTML("title", func(e *colly.HTMLElement) {
        log.Printf("Titre: %s", e.Text)
    })

    c.Visit("https://example.com")
    c.Wait()
}

Cette approche fonctionne pour les cas simples, mais elle a une limitation : le round-robin ne tient pas compte du géo-ciblage dynamique ni des sessions persistantes. Pour un contrôle fin, il faut un SetProxyFunc personnalisé.

SetProxyFunc avec une fonction personnalisée

La signature de SetProxyFunc accepte une func(*http.Request) (*url.URL, error). Vous avez accès à la requête complète — URL, entêtes, contexte — ce qui permet une rotation intelligente basée sur le domaine cible, le pays souhaité, ou un identifiant de session :

func customProxySwitcher(countries []string, password string) colly.ProxyFunc {
    return func(req *http.Request) (*url.URL, error) {
        // Sélection du pays en fonction du domaine cible
        country := pickCountryForDomain(req.URL.Hostname(), countries)

        // Construction dynamique de l'URL du proxy avec géo-ciblage
        proxyURL := fmt.Sprintf(
            "http://user-country-%s-session-%s:%s@gate.proxyhat.com:8080",
            country,
            generateSessionID(),
            password,
        )

        return url.Parse(proxyURL)
    }
}

// Utilisation
c := colly.NewCollector()
c.SetProxyFunc(customProxySwitcher([]string{"DE", "FR", "GB"}, "yourPassword"))

Cette approche est idiomatique : elle s'intègre dans le pipeline de Colly comme un middleware, sans modifier la logique de scraping. Chaque requête peut avoir un pays et une session différents, ce qui est crucial pour les cibles difficiles comme Google SERP ou les sites e-commerce protégés.

Support HTTP et SOCKS5

Colly supporte nativement les schémas http://, https:// et socks5:// dans les URLs de proxy. Pour SOCKS5 avec ProxyHat :

proxyURL := "socks5://user-country-DE-session-abc123:pass@gate.proxyhat.com:1080"

SOCKS5 est utile quand vous avez besoin d'un tunnel TCP de bas niveau, par exemple pour des protocoles non-HTTP ou pour contourner des pare-feu restrictifs. Pour la majorité des cas de scraping web, HTTP sur le port 8080 est suffisant et légèrement plus rapide.

Pourquoi les proxys résidentiels sont nécessaires pour les cibles difficiles

Tous les proxys ne sont pas égaux face aux systèmes anti-bot. Voici une comparaison des trois types principaux :

Critère Résidentiel Datacenter Mobile
Adresse IP FAI réel (AT&T, Orange, Vodafone) Plage d'IPs hébergeur (AWS, OVH) Réseaux mobiles (4G/5G)
Détection anti-bot Faible — ressemble à un utilisateur réel Élevée — flagged instantanément par Cloudflare Très faible — confiance maximale
Vitesse moyenne ~200ms à 800ms ~50ms à 150ms ~300ms à 1200ms
Coût relatif Moyen Bas Élevé
Idéal pour SERP, e-commerce, social Scraping simple, APIs publiques Cibles ultra-protégées

Les proxys datacenter sont détectés par la plupart des services anti-bot modernes car leurs plages d'IP sont publiquement connues et cataloguées. Cloudflare, par exemple, maintient une base de données d'IPs datacenter et les challenge systématiquement. Les proxys résidentiels, en revanche, proviennent d'ISP réels — leur ASN correspond à un fournisseur d'accès grand public, pas à un hébergeur. C'est pourquoi la colly proxy rotation avec des proxys résidentiels est la configuration recommandée pour les cibles difficiles.

Géo-ciblage et sessions avec ProxyHat

ProxyHat permet d'encoder le pays, la ville et l'ID de session directement dans le nom d'utilisateur du proxy. C'est particulièrement puissant combiné avec un SetProxyFunc personnalisé dans Colly :

  • user-country-DE:pass@gate.proxyhat.com:8080 — IP résidentielle en Allemagne
  • user-country-DE-city-berlin:pass@gate.proxyhat.com:8080 — IP à Berlin spécifiquement
  • user-country-DE-session-abc123:pass@gate.proxyhat.com:8080 — session persistante : la même IP est conservée tant que l'ID de session ne change pas

Les sessions persistantes sont cruciales pour les sites qui nécessitent une authentification progressive ou un panier d'achat. Si vous scrapez un site e-commerce qui suit votre session via des cookies IP-bound, changer d'IP à chaque requête casse la session. Avec -session-abc123, ProxyHat vous attribue une IP fixe pour la durée de la session (jusqu'à 30 minutes d'inactivité), puis vous pouvez changer d'ID pour forcer une nouvelle IP.

Exemple Go complet : Collector avec rotation résidentielle

Voici un exemple complet et runnable qui combine un RoundRobinProxySwitcher résidentiel, des règles Limit(), et un switcher personnalisé lisant la liste d'endpoints ProxyHat :

package main

import (
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "log"
    "net/http"
    "net/url"
    "time"

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

func main() {
    // Pool de proxys résidentiels avec géo-ciblage
    proxies := []string{
        "http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080",
        "http://user-country-FR-city-paris:pass@gate.proxyhat.com:8080",
        "http://user-country-GB-city-london:pass@gate.proxyhat.com:8080",
        "http://user-country-NL-city-amsterdam:pass@gate.proxyhat.com:8080",
        "http://user-country-ES-city-madrid:pass@gate.proxyhat.com:8080",
    }

    rp, err := proxy.RoundRobinProxySwitcher(proxies...)
    if err != nil {
        log.Fatalf("Erreur switcher: %v", err)
    }

    c := colly.NewCollector(
        colly.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"),
        colly.MaxDepth(2),
        colly.Async(true),
    )

    // Application du switcher de proxy
    c.SetProxyFunc(rp)

    // Limites de taux : 2 requêtes simultanées par domaine, délai aléatoire 1-3s
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 2,
        Delay:       2 * time.Second,
        RandomDelay: 1 * time.Second,
    })

    // Callback avant envoi : injection d'entêtes
    c.OnRequest(func(r *colly.Request) {
        r.Headers.Set("Accept-Language", "en-US,en;q=0.9,de;q=0.8")
        r.Headers.Set("Accept", "text/html,application/xhtml+xml")
        log.Printf("Visite: %s via proxy", r.URL)
    })

    // Callback HTML : extraction des liens
    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        link := e.Attr("href")
        absoluteURL := e.Request.AbsoluteURL(link)
        if e.Request.Depth < 2 {
            e.Request.Visit(absoluteURL)
        }
    })

    // Callback d'erreur : retry avec proxy différent
    c.OnError(func(r *colly.Response, err error) {
        log.Printf("Erreur sur %s: %v (status %d)", r.Request.URL, err, r.StatusCode)
        if r.StatusCode == 403 || r.StatusCode == 429 {
            // Retry après délai avec un nouveau proxy (nouvelle session)
            time.Sleep(5 * time.Second)
            r.Request.Retry()
        }
    })

    c.OnScraped(func(r *colly.Response) {
        log.Printf("Scraping terminé pour %s", r.Request.URL)
    })

    // Démarrage
    c.Visit("https://example.com")
    c.Wait()
}

Cet exemple illustre plusieurs patterns clés : le mode Async(true) pour le parallélisme, les règles Limit() pour contrôler le taux, le callback OnError pour les retries, et le switcher round-robin pour la rotation.

Switcher personnalisé avec sessions dynamiques

Pour un contrôle plus fin — notamment la rotation de sessions par requête avec géo-ciblage dynamique — voici un switcher personnalisé :

func residentialProxySwitcher(password string, countries []string) colly.ProxyFunc {
    return func(req *http.Request) (*url.URL, error) {
        // Pays aléatoire dans la liste
        idx := rand.Intn(len(countries))
        country := countries[idx]

        // ID de session unique par requête pour rotation d'IP
        sessionBytes := make([]byte, 8)
        rand.Read(sessionBytes)
        sessionID := hex.EncodeToString(sessionBytes)

        proxyStr := fmt.Sprintf(
            "http://user-country-%s-session-%s:%s@gate.proxyhat.com:8080",
            country, sessionID, password,
        )

        return url.Parse(proxyStr)
    }
}

// Utilisation
c := colly.NewCollector(colly.Async(true))
c.SetProxyFunc(residentialProxySwitcher("yourPassword", []string{"DE", "FR", "GB", "NL"}))

Avec ce switcher, chaque requête obtient une IP résidentielle différente dans un pays différent, avec un ID de session unique. C'est la configuration idéale pour le suivi de SERP à grande échelle ou le scraping web multi-zone.

Patterns de production pour Colly avec proxys rotatifs

Retries avec c.Clone()

Colly ne propose pas de retry automatique natif. Le pattern idiomatique consiste à cloner le collector dans OnError pour relancer la requête avec une configuration différente (nouveau proxy, nouveau User-Agent) :

var retryCount = make(map[string]int)
const maxRetries = 3

c.OnError(func(r *colly.Response, err error) {
    urlStr := r.Request.URL.String()
    retryCount[urlStr]++

    if retryCount[urlStr] <= maxRetries {
        log.Printf("Retry %d/%d pour %s", retryCount[urlStr], maxRetries, urlStr)

        // Clone avec un nouveau proxy
        cClone := r.Request.C.Clone()
        cClone.SetProxyFunc(residentialProxySwitcher("pass", []string{"DE", "FR"}))

        time.Sleep(time.Duration(retryCount[urlStr]*2) * time.Second)
        cClone.Visit(urlStr)
    }
})

Le Clone() duplique la configuration du collector (callbacks, limites, transport) tout en permettant de modifier la fonction de proxy. C'est plus propre que de modifier le collector original à chaud.

RandomDelay et limites de parallélisme

Les règles Limit() sont votre premier outil contre les blocages. La combinaison de Delay et RandomDelay ajoute de la variance humaine :

c.Limit(&colly.LimitRule{
    DomainGlob:  "*.example.com",
    Parallelism: 5,
    Delay:       3 * time.Second,
    RandomDelay: 2 * time.Second,
})

Avec cette configuration, Colly attend 3 à 5 secondes entre chaque requête vers le même domaine, avec au maximum 5 requêtes simultanées. Le RandomDelay est crucial : un délai fixe est détectable par les systèmes anti-bot, alors qu'un délai aléatoire ressemble à un comportement humain.

Transport TLS personnalisé

Pour éviter le fingerprinting TLS (JA3), vous pouvez personnaliser le http.Transport sous-jacent :

transport := &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,
}

c := colly.NewCollector()
c.WithTransport(transport)

Le fingerprinting JA3 est utilisé par Cloudflare et d'autres WAF pour identifier les clients non-navigateurs. En personnalisant les cipher suites et les versions TLS, vous réduisez le risque de détection, surtout combiné avec des proxys résidentiels.

Scraping distribué avec stockage Redis

Colly supporte plusieurs backends de stockage pour la file d'attente des URLs visitées. Pour le scraping distribué sur plusieurs instances, le backend Redis est essentiel :

import "github.com/gocolly/colly/v2/storage"

// Stockage Redis pour coordination distribuée
redisStorage := &storage.RedisStorage{
    Address:  "redis://localhost:6379",
    Prefix:   "colly_scraper:",
}

if err := redisStorage.Init(); err != nil {
    log.Fatal(err)
}

c := colly.NewCollector()
c.SetStorage(redisStorage)

Avec Redis, plusieurs instances de votre scraper Go peuvent partager la file d'attente et le tracking des URLs visitées. Chaque instance prend une URL de la file Redis, la scrape, et ajoute les nouveaux liens trouvés. C'est le pattern recommandé pour dépasser 1 000 requêtes par seconde avec Colly.

Consultez la documentation officielle de Colly pour les détails complets du backend de stockage, et la documentation ProxyHat pour les paramètres de géo-ciblage avancés.

Quand NE PAS utiliser Colly

Colly est excellent pour le scraping de pages HTML statiques et rendues côté serveur. Mais il ne gère pas le JavaScript. Si votre cible est une Single Page Application (React, Vue, Angular) qui charge son contenu via des appels API XHR/fetch après le rendu initial, Colly ne verra qu'un shell HTML vide.

Dans ce cas, vous avez besoin d'un navigateur headless. Les options en Go sont limitées — chromedp (basé sur Chrome DevTools Protocol) est le plus mature. Vous pouvez combiner Colly pour la découverte d'URLs et chromedp pour le rendu des pages JS-heavy, mais c'est une architecture plus complexe.

Une alternative hybride : utilisez Colly pour intercepter les appels API sous-jacents. Beaucoup de SPAs exposent des endpoints JSON que vous pouvez appeler directement avec Colly, sans avoir besoin de rendre le JavaScript. Inspectez le trafic réseau avec les DevTools du navigateur pour identifier ces endpoints.

Considérations éthiques et légales

Le scraping doit être pratiqué de manière responsable :

  • Respectez robots.txt — Colly peut le faire automatiquement avec colly.IgnoreRobotsTxt(false) (par défaut, Colly respecte robots.txt).
  • Limitez votre taux — même avec des proxys rotatifs, un taux excessif peut surcharger l'infrastructure cible. Restez sous 5 requêtes par seconde par domaine.
  • Scrapez uniquement des données publiques — les données derrière un login ou paywall sont soumises à des conditions d'utilisation spécifiques.
  • RGPD et CCPA — le scraping de données personnelles (noms, emails, téléphones) est soumis aux réglementations sur la protection des données. Consultez le texte du RGPD pour les obligations.
  • CFAA — aux États-Unis, le Computer Fraud and Abuse Act peut s'appliquer au scraping non autorisé. La jurisprudence (Van Buren v. United States, 2021) a clarifié certains aspects, mais la prudence reste de mise.

Tableau récapitulatif : stratégies de rotation dans Colly

Stratégie Quand l'utiliser Avantages Inconvénients
RoundRobinProxySwitcher Pool fixe, cibles simples Simple, natif Colly Pas de géo-ciblage dynamique
SetProxyFunc personnalisé Géo-ciblage, sessions Contrôle total par requête Plus de code à maintenir
Sessions persistantes Sites avec auth/panier IP stable pendant la session Moins de rotation d'IP
Rotation par requête SERP, e-commerce massif Max de diversité d'IP Risque de patterns détectables

Points clés à retenir

La rotation de proxies dans Colly n'est pas un hack — c'est un middleware idiomatique. Utilisez SetProxyFunc avec une fonction personnalisée pour un contrôle total, ou RoundRobinProxySwitcher pour les cas simples.

  • Préférez les proxys résidentiels pour les cibles difficiles. Les IPs datacenter sont flaggées par Cloudflare et similires en moins de 5 requêtes.
  • Géo-ciblez avec ProxyHat via le nom d'utilisateur : user-country-DE-city-berlin-session-abc123:pass@gate.proxyhat.com:8080.
  • Combinez Limit() + RandomDelay pour simuler un comportement humain. Un délai aléatoire de 1 à 3 secondes par domaine réduit drastiquement les blocages.
  • Utilisez c.Clone() pour les retries — ne modifiez jamais un collector en cours d'exécution.
  • Passez au stockage Redis pour le scraping distribué au-delà de 1 000 requêtes par seconde.
  • Colly ne gère pas le JS — pour les SPAs, utilisez chromedp ou interceptez les API calls directement.
  • Respectez robots.txt et la loi — RGPD, CFAA, et conditions d'utilisation s'appliquent au scraping automatisé.

Prêt à mettre en place votre infrastructure de scraping avec proxys résidentiels ? Consultez notre page de tarification pour les plans résidentiels, explorez nos localisations disponibles, ou consultez la documentation ProxyHat pour les détails techniques complets.

Questions fréquentes

Qu'est-ce que la rotation de proxies dans Colly ?

La rotation de proxies dans Colly consiste à utiliser les points d'extension natifs du framework — proxy.RoundRobinProxySwitcher et SetProxyFunc — pour distribuer les requêtes HTTP across un pool d'adresses IP. Colly expose une interface func(*http.Request) (*url.URL, error) qui s'intègre dans le pipeline de requêtes comme un middleware, permettant de changer d'IP à chaque requête ou de maintenir des sessions persistantes avec géo-ciblage.

Pourquoi la rotation de proxies dans Colly est-elle importante ?

La rotation de proxies est essentielle car les systèmes anti-bot modernes comme Cloudflare et Datadome détectent et bloquent les IPs qui envoient un volume élevé de requêtes. Sans rotation, un scraper Go qui envoie plus de 50 requêtes par minute vers un même domaine se fait bloquer quasi-systématiquement. La rotation de proxys résidentiels distribue les requêtes across des IPs d'ISP réels, rendant le trafic indiscernable de celui d'utilisateurs normaux.

Quel type de proxy fonctionne le mieux pour la rotation dans Colly ?

Les proxys résidentiels sont le meilleur choix pour la rotation dans Colly lorsqu'on cible des sites protégés par des anti-bot. Contrairement aux proxys datacenter dont les plages d'IP sont publiquement cataloguées et flaggées instantanément, les proxys résidentiels proviennent d'ISP réels avec un ASN grand public. Pour les cibles ultra-protégées, les proxys mobiles 4G/5G offrent la confiance maximale mais à un coût plus élevé et une latence supérieure.

Comment éviter les blocages lors de la rotation de proxies dans Colly ?

Pour éviter les blocages, combinez plusieurs stratégies : utilisez des proxys résidentiels avec géo-ciblage (user-country-DE-city-berlin), configurez des règles Limit() avec Delay et RandomDelay pour simuler un comportement humain (1 à 3 secondes de variance), personnalisez le Transport TLS pour éviter le fingerprinting JA3, utilisez c.Clone() pour les retries avec un proxy différent, et respectez toujours le robots.txt. Un parallélisme de 2 à 5 requêtes simultanées par domaine est un bon point de départ.

Colly supporte-t-il les proxys SOCKS5 ?

Oui, Colly supporte nativement les proxys SOCKS5 via le schéma socks5:// dans les URLs de proxy. Avec ProxyHat, vous pouvez utiliser socks5://user-country-DE-session-abc123:pass@gate.proxyhat.com:1080. SOCKS5 est utile pour les tunnels TCP de bas niveau ou pour contourner des pare-feu restrictifs, mais pour la majorité des cas de scraping web, HTTP sur le port 8080 est suffisant et légèrement plus rapide.

Prêt à commencer ?

Proxys résidentiels, ISP et mobiles dans plus de 148 pays. Créez un compte gratuit.

Créer un compte gratuit
← Retour au Blog