Rotierende Proxys in Colly: Was das Framework bietet
Colly ist das populärste Web-Scraping-Framework für Go und verbindet eine Callback-basierte Collector-API mit der goquery-gestützten DOM-Traversierung. Wenn du rotierende Proxys in Colly einsetzen willst, brauchst du keine externen Libraries — das Framework bringt mit proxy.RoundRobinProxySwitcher und c.SetProxyFunc bereits zwei idiomatische Einstiegspunkte mit. In diesem Guide zeigen wir, wie du diese Oberflächen korrekt nutzt, Residential-Proxys von ProxyHat pro Request rotierst und einen produktionsreifen Scraper baust.
Bevor wir in den Code gehen, ein wichtiger Hinweis: Scraping öffentlicher Daten unterliegt rechtlichen Rahmenbedingungen. In den USA kann der Computer Fraud and Abuse Act (CFAA) bei Überschreitung von Nutzungsbedingungen relevant werden, und in der EU greift die DSGVO (GDPR), sobald personenbezogene Daten verarbeitet werden. Respektiere robots.txt, rate-limit deine Requests und scrap ausschließlich öffentlich zugängliche Inhalte.
Collys Collector-Modell verstehen
Ein colly.Collector ist die zentrale Einheit in Colly. Er verwaltet HTTP-Requests, Callbacks, Storage-Backends und Middleware-Hooks. Das Callback-Modell folgt dem klassischen Scraper-Pattern: OnRequest, OnHTML, OnError und OnResponse werden registriert und bei jedem passenden Event ausgeführt.
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (compatible; MyBot/1.0)"),
colly.MaxDepth(2),
)
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL)
})
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
link := e.Attr("href")
e.Request.Visit(link)
})
c.OnError(func(r *colly.Response, err error) {
fmt.Printf("Fehler bei %s: %v\n", r.Request.URL, err)
})
Colly nutzt goquery für die DOM-Traversierung, das wiederum auf net/html aufbaut. Das bedeutet: e.DOM ist ein goquery-Document, und du kannst die gesamte jQuery-ähnliche API nutzen. Der HTMLElement-Callback wird für jeden Selektor-Match einmal aufgerufen, was das Extrahieren von Listen, Tabellen und verschachtelten Strukturen sehr elegant macht.
Async-Modus und Parallelität
Standardmäßig arbeitet Colly synchron. Mit c.Async = true schaltest du den asynchronen Modus ein, der Requests über einen internen Goroutinen-Pool abwickelt. Wichtig: Im Async-Modus musst du c.Wait() aufrufen, um auf den Abschluss aller in-flight Requests zu warten. Die Parallelität steuerst du mit c.Limit(&colly.LimitRule{...}).
c.Async = true
err := c.Limit(&colly.LimitRule{
DomainGlob: "*",
Parallelism: 20,
Delay: 1 * time.Second,
RandomDelay: 500 * time.Millisecond,
})
if err != nil {
log.Fatal(err)
}
Hier setzen wir Parallelism auf 20 und kombinieren ein festes Delay von 1 Sekunde mit einem zufälligen Jitter von 500 ms. Das ist ein realistischer Wert für die meisten Ziele — zu aggressive Werte führen zu 429-Antworten und IP-Sperren.
Die idiomatische Proxy-Oberfläche von Colly
Colly bietet zwei Wege, Proxys zu konfigurieren: den eingebauten RoundRobinProxySwitcher und die niedrigschwelligere SetProxyFunc-API. Beide arbeiten auf der Ebene von *http.Request und liefern eine *url.URL zurück, die Go dann als Proxy-URL verwendet.
Variante 1: RoundRobinProxySwitcher
Der proxy.RoundRobinProxySwitcher ist die einfachste Methode für statische Proxy-Listen. Du übergibst eine Liste von Proxy-URLs und Colly rotiert im Round-Robin-Verfahren durch sie:
import "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)
Diese Methode ist idiomatisch, weil sie direkt die Colly-Middleware-Schnittstelle implementiert — kein Hack, kein Patchen des Transports. Der Switcher wird pro Request aufgerufen und liefert die nächste URL in der Liste.
Variante 2: Benutzerdefinierte SetProxyFunc
Für fortgeschrittene Szenarien — dynamische Proxy-Listen, Geo-Targeting pro Request, Session-Stickiness — schreibst du eine eigene Funktion mit der Signatur func(*http.Request) (*url.URL, error):
c.SetProxyFunc(func(r *http.Request) (*url.URL, error) {
sessionID := fmt.Sprintf("sess-%d", rand.Intn(100000))
proxyURL := fmt.Sprintf(
"http://user-session-%s-country-DE:pass@gate.proxyhat.com:8080",
sessionID,
)
return url.Parse(proxyURL)
})
Diese Funktion wird für jeden einzelnen Request aufgerufen, sodass du pro Request eine neue Session und ein neues Land wählen kannst. Das ist der Schlüssel zu echter IP-Rotation auf Residential-Ebene.
Warum Residential-Proxys für harte Ziele notwendig sind
Datacenter-Proxys sind schnell und günstig, aber ihre IP-Bereiche sind öffentlich bekannt und werden von Anti-Bot-Diensten wie Cloudflare, Datadome und PerimeterX blockiert. Residential-Proxys verwenden IPs echter ISPs und sind daher wesentlich schwerer zu erkennen. Für SERP-Tracking, E-Commerce-Preisüberwachung und Social-Media-Research sind sie praktisch unverzichtbar.
Mit ProxyHat kannst du Geo-Targeting und Sessions direkt im Benutzernamen steuern. Die Formate sind:
user-country-DE-city-berlin:pass@gate.proxyhat.com:8080— Stadt-Level-Targetinguser-session-abc123:pass@gate.proxyhat.com:8080— Sticky Session, gleiche IP für mehrere Requestsuser-country-US-session-sess42:pass@gate.proxyhat.com:8080— Kombination aus Land und Session
Die -session-xxx-Flag ist besonders wichtig: Wenn du mehrere Requests in einem Login-Flow oder einer Paginierung abschickst, willst du oft dieselbe IP behalten. Rotierst du mitten im Flow, bricht die Session. Für reine Massen-Scraping-Jobs ohne State wechselst du die Session-ID pro Request und bekommst automatisch eine neue IP.
Runnable Go-Beispiel: Residential Proxy Switcher
Hier ist ein vollständiges, kompilierbares Beispiel, das einen Collector mit einem benutzerdefinierten Switcher für ProxyHat-Residential-Proxys konfiguriert. Der Switcher rotiert durch verschiedene Länder und generiert pro Request eine neue Session-ID.
package main
import (
"fmt"
"math/rand"
"net/url"
"time"
"github.com/gocolly/colly/v2"
)
const (
proxyUser = "your-user"
proxyPass = "your-pass"
gateway = "gate.proxyhat.com:8080"
)
var countries = []string{"DE", "US", "GB", "FR", "JP"}
func residentialSwitcher(r *colly.Request) (*url.URL, error) {
country := countries[rand.Intn(len(countries))]
session := fmt.Sprintf("sess-%d", rand.Intn(999999))
raw := fmt.Sprintf(
"http://%s-country-%s-session-%s:%s@%s",
proxyUser, country, session, proxyPass, gateway,
)
return url.Parse(raw)
}
func main() {
rand.Seed(time.Now().UnixNano())
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (compatible; ResearchBot/1.0)"),
colly.MaxDepth(1),
)
c.Async = true
c.Limit(&colly.LimitRule{
DomainGlob: "*",
Parallelism: 20,
Delay: 800 * time.Millisecond,
RandomDelay: 400 * time.Millisecond,
})
c.SetProxyFunc(residentialSwitcher)
c.OnRequest(func(r *colly.Request) {
fmt.Printf("[%s] → %s\n", time.Now().Format("15:04:05"), r.URL)
})
c.OnHTML("title", func(e *colly.HTMLElement) {
fmt.Printf("Titel: %s\n", e.Text)
})
c.OnError(func(r *colly.Response, err error) {
fmt.Printf("Fehler %d bei %s: %v\n", r.StatusCode, r.Request.URL, err)
})
targets := []string{
"https://httpbin.org/ip",
"https://httpbin.org/headers",
"https://httpbin.org/user-agent",
}
for _, t := range targets {
c.Visit(t)
}
c.Wait()
}
Dieses Beispiel zeigt mehrere idiomatische Colly-Patterns in Aktion: den asynchronen Modus, Limit-Regeln mit Jitter, einen benutzerdefinierten Proxy-Switcher und die Callback-Registrierung. Die RandomDelay von 400 ms sorgt dafür, dass deine Requests nicht wie ein Bot im Takt feuern.
ProxyHat SDK-Endpoint-Liste laden
Wenn du statt hartkodierter Länder eine dynamische Endpoint-Liste von ProxyHat laden willst, kannst du die ProxyHat-Dokumentation konsultieren und die Liste zur Laufzeit abrufen. Ein typischer Ansatz:
type ProxyEndpoint struct {
Country string
City string
Session string
}
func loadEndpoints() ([]ProxyEndpoint, error) {
// Beispiel: aus einer API oder Config-Datei laden
return []ProxyEndpoint{
{Country: "DE", City: "berlin"},
{Country: "US", City: "new-york"},
{Country: "JP", City: "tokyo"},
}, nil
}
func buildSwitcher(endpoints []ProxyEndpoint) colly.ProxyFunc {
return func(r *http.Request) (*url.URL, error) {
ep := endpoints[rand.Intn(len(endpoints))]
session := fmt.Sprintf("sess-%d", rand.Intn(999999))
raw := fmt.Sprintf(
"http://%s-country-%s-city-%s-session-%s:%s@%s",
proxyUser, ep.Country, ep.City, session, proxyPass, gateway,
)
return url.Parse(raw)
}
}
Produktionsmuster: Retries, TLS und verteiltes Scraping
Ein produktionsreifer Scraper muss mit 429/503-Antworten umgehen, TLS-Besonderheiten berücksichtigen und über mehrere Instanzen skalieren können. Colly bietet dafür mehrere Erweiterungspunkte.
Retries mit c.Clone()
Colly hat keine eingebaute Retry-Logik, aber du kannst c.Clone() verwenden, um einen zweiten Collector mit angepasster Konfiguration zu erzeugen. Ein gängiges Pattern ist, bei einem 503-Status den Request nach einer kurzen Pause erneut abzuschicken:
c.OnError(func(r *colly.Response, err error) {
if r.StatusCode == 503 || r.StatusCode == 429 {
retry := c.Clone()
retry.SetProxyFunc(residentialSwitcher)
time.Sleep(2 * time.Second)
retry.Visit(r.Request.URL.String())
retry.Wait()
}
})
Vorsicht: Rekursion kann zu endlosen Schleifen führen. Implementiere einen Zähler im Request-Context, um die Anzahl der Retries zu begrenzen — typischerweise 3 Versuche.
Custom Transport mit TLS-Konfiguration
Manche Ziele erfordern spezifische TLS-Einstellungen, um nicht als Bot erkannt zu werden. Du kannst Collys zugrundeliegenden Transport über http.Client anpassen:
c.WithTransport(&http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
MinVersion: tls.VersionTLS12,
},
MaxIdleConns: 100,
MaxIdleConnsPerHost: 20,
IdleConnTimeout: 90 * time.Second,
})
Setze InsecureSkipVerify nie auf true in Produktion — das öffnet Man-in-the-Middle-Angriffen Tür und Tor. Die obige Konfiguration erzwingt TLS 1.2 als Minimum und hält 20 Idle-Verbindungen pro Host offen.
Verteiltes Scraping mit Redis-Backend
Wenn du über mehrere Maschinen skalierst, brauchst du einen geteilten State für die URL-Queue und besuchte URLs. Colly unterstützt das über Storage-Backends. Mit Redis teilen mehrere Collector-Instanzen dieselbe Dedup-Liste:
import "github.com/gocolly/redisstorage"
storage := &redisstorage.Storage{
Address: "redis://localhost:6379",
Prefix: "my_scraper",
}
c.SetStorage(storage)
Dadurch werden besuchte URLs und die Request-Queue in Redis gespeichert. Jede Worker-Instanz holt sich neue URLs aus der Queue und trägt besuchte URLs ein. Das ist die Basis für containerisierte Scraping-Flotten mit Docker oder Kubernetes, bei denen jeder Pod einen eigenen Collector betreibt, aber alle denselben State teilen.
Headless-Browser-Fleet und Containerisierung
Für noch höhere Durchsätze kombinierst du Colly mit einem Headless-Browser-Fleet. Ein typisches Setup: Ein Go-Service orchestriert Colly-Collectoren in Containern, jeder mit 20–50 parallelen Verbindungen. Mit 10 Containern und 50 Verbindungen pro Container erreichst du 500 gleichzeitige Requests. Bei einer durchschnittlichen Latenz von 200 ms pro Request über Residential-Proxys entspricht das etwa 2.500 Requests pro Sekunde.
SOCKS5-Unterstützung in Colly
Colly unterstützt auch SOCKS5-Proxys. ProxyHat stellt den SOCKS5-Port 1080 zur Verfügung. Die URL-Syntax ändert sich nur im Schema:
socks5://user-country-DE-session-abc123:pass@gate.proxyhat.com:1080
SOCKS5 ist nützlich, wenn du TCP-Tunnel jenseits von HTTP brauchst oder wenn dein Ziel SOCKS5 bevorzugt. Für reines HTTP/HTTPS-Scraping ist Port 8080 in der Regel die bessere Wahl, da der Overhead geringer ist.
Vergleich: Proxy-Typen für Colly
| Eigenschaft | Datacenter | Residential | Mobile |
|---|---|---|---|
| Geschwindigkeit | Sehr hoch (50–100 ms) | Mittel (200–800 ms) | Niedrig (500–2000 ms) |
| Erkennungsrisiko | Hoch | Niedrig | Sehr niedrig |
| Preis pro GB | $0,50–$2 | $3–$15 | $10–$30 |
| Einsatzgebiet | Einfache Ziele | Harte Anti-Bot-Ziele | Mobile-App-APIs |
| Geo-Targeting | Eingeschränkt | Land/Stadt/ASN | Land/Mobilfunkanbieter |
Für die meisten Scraping-Aufgaben mit Colly sind Residential-Proxys der Sweet Spot. Sie bieten die beste Balance zwischen Zuverlässigkeit und Kosten. Mobile-Proxys sind für sehr harte Ziele wie Social-Media-Plattformen relevant, kosten aber deutlich mehr.
Wann du Colly NICHT verwenden solltest
Colly ist ein HTTP-Client-Scraper — es führt kein JavaScript aus. Wenn deine Ziel-Website eine Single-Page-Application ist, die Inhalte erst nach dem Initial-Load via JavaScript rendert (React, Vue, Angular), bekommst du mit Colly nur ein leeres Shell-Dokument. In diesen Fällen brauchst du einen Headless-Browser wie Playwright für Go oder Chromedp.
Ein typisches Hybrid-Pattern: Nutze Colly für die schnelle Entdeckung von URLs und Metadaten aus dem statischen HTML, und leite nur die Seiten, die JS benötigen, an einen Browser-Fleet weiter. So kombinierst du die Geschwindigkeit von Colly mit der Kompatibilität eines Browsers.
Ethische und rechtliche Überlegungen
Web-Scraping ist legal, solange du öffentlich zugängliche Daten extrahierst und keine Nutzungsbedingungen verletzt. Aber die Grenzen sind nicht immer klar. Der CFAA wurde in mehreren Fällen gegen Scraper-Betreiber angewandt, insbesondere wenn Authentifizierungssysteme umgangen wurden. In der EU greift die DSGVO, wenn du personenbezogene Daten verarbeitest — etwa Profil-Daten von Social-Media-Nutzern.
Best Practices:
- Prüfe
robots.txtvor dem Scraping — Colly kann dies mitcolly.URLFiltersund einer Vorab-Prüfung kombinieren. - Setze vernünftige Rate-Limits: 1 Request alle 1–3 Sekunden pro Domain ist ein sicherer Startwert.
- Identifiziere deinen Bot mit einem ehrlichen User-Agent und, falls möglich, einem Kontakt-Header.
- Speichere keine personenbezogenen Daten, es sei denn, du hast eine Rechtsgrundlage.
ProxyHat-spezifische Einrichtung
ProxyHat stellt Residential-, Mobile- und Datacenter-Proxys über dasselbe Gateway bereit. Die Konfiguration erfolgt vollständig über den Benutzernamen. Hier ist ein Beispiel für einen produktionsreifen Collector mit Geo-Targeting und Session-Management:
func geoSwitcher(country, city string) colly.ProxyFunc {
return func(r *http.Request) (*url.URL, error) {
session := fmt.Sprintf("geo-%d", time.Now().UnixNano())
raw := fmt.Sprintf(
"http://%s-country-%s-city-%s-session-%s:%s@gate.proxyhat.com:8080",
proxyUser, country, city, session, proxyPass,
)
return url.Parse(raw)
}
}
// Für SERP-Tracking in Deutschland
c.SetProxyFunc(geoSwitcher("DE", "berlin"))
Diese Konfiguration ist ideal für SERP-Tracking und Web-Scraping, bei dem du länderspezifische Ergebnisse brauchst. Die verfügbaren Länder und Städte findest du auf der ProxyHat-Locations-Seite. Preise und Pakete findest du unter /de/pricing.
Key Takeaways
- Idiomatische Integration: Nutze
proxy.RoundRobinProxySwitcherfür statische Listen undc.SetProxyFuncfür dynamische Rotation — beide sind erste-Klasse-Bürger in Collys Middleware.- Residential-Proxys sind Pflicht für harte Ziele: Datacenter-IPs werden von modernen Anti-Bot-Systemen zuverlässig erkannt. ProxyHat-Residential-Proxys mit
-country-XXund-session-xxxim Benutzernamen liefern echte ISP-IPs.- Rate-Limiting schützt deine IPs:
Limit()mitParallelism,DelayundRandomDelayist die wichtigste Verteidigung gegen 429-Antworten. Startwerte: 20 parallele Requests, 800 ms Delay, 400 ms Jitter.- Retries mit Clone(): Bei 503/429 lohnt sich ein Retry mit neuer Session-ID, aber begrenze die Versuche auf 3, um Endlosschleifen zu vermeiden.
- Verteiltes Scraping über Redis: Collys Storage-Backends ermöglichen horizontale Skalierung über Container und Pods mit geteiltem Dedup-State.
- Colly ist kein Browser: Für JS-SPAs brauchst du Playwright oder Chromedp. Kombiniere beide für ein hybrides Setup.
Fazit und nächste Schritte
Rotierende Proxys in Colly sind kein Hack, sondern ein idiomatischer Bestandteil des Frameworks. Mit SetProxyFunc und den ProxyHat-Benutzernamen-Flags hast du ein mächtiges Werkzeug, um Scraper zu bauen, die auch gegen harte Anti-Bot-Systeme bestehen. Beginne mit dem oben gezeigten Beispiel, passe die Limit-Regeln an dein Ziel an und skaliere über Redis-Backends, wenn du mehrere Maschinen betreibst.
Wenn du bereit bist, loszulegen, besuche die ProxyHat-Preisseite für Paketoptionen oder die ProxyHat-Dokumentation für detaillierte API-Referenzen.






