# Go Proxy Scraping: http.Transport, uwierzytelnianie, rotacja i współbieżność

W Go cała historia proxy opiera się na jednym polu: Transport.Proxy. Ustaw je poprawnie, a otrzymasz rotację per-request, SOCKS5 i colly za darmo. Ustaw je źle, a o 2 w nocy otrzymasz 'context canceled'.

Go sprawia, że proxy jest zwodniczo proste: cała historia po stronie klienta opiera się na jednym polu, `http.Transport.Proxy`. Ustaw je poprawnie, a rotacja per-request, SOCKS5 i integracja z `colly` wynikają z tego samego projektu. Ustaw je źle, a napotkasz klasyczne błędy proxy Go - `context canceled`, wyczerpanie puli połączeń i zmienną `HTTP_PROXY`, którą twój niestandardowy transport cicho ignoruje. Ten przewodnik przeprowadza przez całą ścieżkę z kodem gotowym do uruchomienia.

## Jednolinijkowiec: http.Transport z URL proxy

Każde żądanie przez proxy w Go przepływa przez `Transport`. Pakiet `net/http` dostarcza pomocnika, `http.ProxyURL`, który przypisuje jedno statyczne proxy dla całego klienta. Dane uwierzytelniające trafiają bezpośrednio do URL userinfo - Go zamienia je na nagłówek `Proxy-Authorization` za ciebie:

```go
package main

import (
  "fmt"
  "net/http"
  "net/url"
  "time"
)

func main() {
  proxyURL, _ := url.Parse("http://USER:PASS@residentialpremium.quantumproxies.io:8080")
  client := &http.Client{
    Timeout:   20 * time.Second,
    Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
  }
  resp, err := client.Get("https://httpbin.org/ip")
  if err != nil {
    panic(err)
  }
  defer resp.Body.Close()
  fmt.Println(resp.Status) // body shows the exit IP, not yours
}
```

Zawsze ustawiaj `Client.Timeout`. Go nie ma domyślnego limitu czasu na `http.Client`, więc jedno martwe wyjście zablokuje goroutine na zawsze - głównym powodem, dla którego skrobak Go wydaje się zawieszać, jest brakujący limit czasu, a nie wolny cel.

## Rotacja proxy z funkcją Proxy

`Proxy` nie jest ograniczone do stałego URL - jest to funkcja `func(*http.Request) (*url.URL, error)`, którą Go wywołuje raz na żądanie. To jest twój haczyk rotacyjny. Wybierz losowy punkt końcowy z puli, a każde żądanie wychodzi przez inny IP, bez żadnej logiki zarządzania listą w pętli pobierania:

```go
import "math/rand"

// three sticky sessions on the same gateway = three exit IPs to pick from
pool := []string{
  "http://USER-sid-pool1-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
  "http://USER-sid-pool2-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
  "http://USER-sid-pool3-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
}

transport := &http.Transport{
  Proxy: func(r *http.Request) (*url.URL, error) {
    return url.Parse(pool[rand.Intn(len(pool))])
  },
  MaxIdleConnsPerHost: 32, // reuse connections across the pool
}
```

W większości przypadków skrobania nie powinieneś w ogóle zarządzać pulą. Skieruj funkcję `Proxy` na pojedynczy [rotacyjny bramowy punkt dostępowy](https://quantumproxies.io/rotating-proxies), a brama dostarczy ci świeży IP przy każdym żądaniu z ponad 90 milionów adresów w ponad 200 krajach - jeden punkt końcowy, brak listy, sesje lepkie, gdy przepływ wymaga tego samego wyjścia przez kilka minut.

![Jeden http.Client w Go z jednym Transport, którego funkcja Proxy uruchamia się dla każdego żądania, więc żądania 1, 2 i 3 wychodzą przez różne IP wyjściowe do tego samego celu. Karta: funkcja jest wywoływana raz na żądanie, wybiera z puli lub wskazuje jedną rotującą bramę, a MaxIdleConnsPerHost: 32 zapewnia ponowne użycie połączeń.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335148/blog/generated/qp-b9-009-diag1.png)
*Jeden Transport, jedna funkcja Proxy: funkcja działa per request, więc jeden klient rotuje przez całą pulę.*

## Uwierzytelnianie proxy w Go

Umieszczenie `USER:PASS@` w URL proxy to czysta ścieżka i działa zarówno dla `ProxyURL`, jak i niestandardowej funkcji `Proxy`. Jeśli wolisz trzymać dane uwierzytelniające poza URL, ustaw nagłówek na tunelu CONNECT samodzielnie za pomocą `Transport.ProxyConnectHeader`. Jeśli twój dostawca używa uwierzytelniania na białej liście IP, całkowicie pomiń dane uwierzytelniające i autoryzuj IP swojego serwera w panelu - QuantumProxies obsługuje oba. Odmowa CONNECT z `407` lub `proxyconnect tcp` prawie zawsze oznacza brakujące lub błędne dane uwierzytelniające, a nie blokadę po stronie celu.

## Zmienna środowiskowa HTTP_PROXY (i kiedy Go ją ignoruje)

Go's `http.DefaultTransport` używa `http.ProxyFromEnvironment`, który odczytuje `HTTP_PROXY`, `HTTPS_PROXY` i `NO_PROXY` (zgodnie z pakietem `golang.org/x/net/http/httpproxy`). Dwa zachowania wprowadzają ludzi w błąd. Po pierwsze, te zmienne akceptują pełny URL lub nagi `host:port`, gdzie zakłada się schemat `http`. Po drugie, żądania do `localhost` lub adresu pętli zwrotnej zawsze omijają proxy, zwracając pusty URL. Większym problemem jest odwrotność: w momencie, gdy tworzysz własny `&http.Transport{Proxy: ...}`, *zastępujesz* zachowanie zmiennych środowiskowych - twoja jawna funkcja wygrywa i `HTTP_PROXY` jest ignorowane. Jeśli chcesz obu, opakuj je samodzielnie.

## SOCKS5 proxy w Go

Biblioteka standardowa nie ma klienta SOCKS5, więc pobierz `golang.org/x/net/proxy` i wybierz przez niego. Podaj dialer do `Transport.DialContext`, aby DNS rozwiązywał się w proxy, a nie na twoim komputerze (ten sam problem z wyciekiem nazw hostów, który `socks5h` rozwiązuje w innych stosach). Jeśli rozważasz oba schematy, nasza uwaga na temat [SOCKS5 vs HTTP proxies](https://quantumproxies.io/blog/socks5-vs-http-proxy-difference) wyjaśnia, kiedy każdy z nich wygrywa:

```bash
go get golang.org/x/net/proxy
```

```go
import (
  "context"
  "net"
  "net/http"
  "golang.org/x/net/proxy"
)

auth := &proxy.Auth{User: "USER", Password: "PASS"}
dialer, err := proxy.SOCKS5("tcp", "residentialpremium.quantumproxies.io:1080", auth, proxy.Direct)
if err != nil {
  panic(err)
}

transport := &http.Transport{
  DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
    return dialer.Dial(network, addr)
  },
}
client := &http.Client{Transport: transport}
```

## Proxy w colly

Jeśli skrobiesz za pomocą `colly`, otrzymujesz rotację bez dotykania transportu. Pakiet `colly/proxy` dostarcza `RoundRobinProxySwitcher`, który cyklicznie zmienia punkty końcowe per request, a `LimitRule` zarządza tempem. To najszybszy sposób na uprzejmego, rotacyjnego skrobaka w Go:

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

c := colly.NewCollector(colly.Async(true))

rp, err := proxy.RoundRobinProxySwitcher(
  "http://USER-sid-crawl1-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
  "http://USER-sid-crawl2-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080",
)
if err != nil {
  panic(err)
}
c.SetProxyFunc(rp)

c.Limit(&colly.LimitRule{
  DomainGlob:  "*",
  Parallelism: 8,
  RandomDelay: 2 * time.Second,
})
```

## Współbieżność zrobiona dobrze (i pułapka context-canceled)

Współbieżność Go to powód, dla którego zespoły wybierają go do skrobania, a także źródło błędu `context canceled`. Występuje, gdy kontekst żądania zostanie anulowany przed pełnym odczytaniem ciała - zazwyczaj jest to wygasły limit czasu per request lub `cancel()`, który uruchomił się zbyt wcześnie z powodu błędnego `defer`. Ogranicz fan-out za pomocą semafora, daj każdemu żądaniu własny kontekst limitu czasu, a następnie odczytaj i zamknij ciało przed uruchomieniem anulowania:

```go
sem := make(chan struct{}, 20) // cap concurrency
var wg sync.WaitGroup

for _, u := range urls {
  wg.Add(1)
  sem <- struct{}{}
  go func(u string) {
    defer wg.Done()
    defer func() { <-sem }()

    ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
    defer cancel()

    req, _ := http.NewRequestWithContext(ctx, "GET", u, nil)
    resp, err := client.Do(req)
    if err != nil {
      return // rotate/log; do not retry the same burned exit
    }
    io.Copy(io.Discard, resp.Body) // drain BEFORE cancel fires
    resp.Body.Close()
  }(u)
}
wg.Wait()
```

Dwa dodatkowe uwagi produkcyjne. Ustaw `MaxIdleConnsPerHost` wystarczająco wysoko, aby duża pula ponownie wykorzystywała połączenia zamiast wyczerpywać deskryptory plików - zwykła przyczyna `EOF` po kilku setkach żądań. I rotuj wyjście w przypadku awarii zamiast ponawiania tego samego; martwy IP rezydencyjny nie odzyska się przy następnej próbie. Dla szerszego projektu na milionach stron, nasz przewodnik na temat [architektury skrobania na dużą skalę](https://quantumproxies.io/blog/large-scale-web-scraping-architecture) obejmuje kolejki, deduplikację i warstwowanie proxy.

## Ponowne użycie jednego klienta, dostrojenie transportu

Utwórz `http.Client` raz i udostępnij go we wszystkich goroutines - jest bezpieczny do współbieżnego użycia, a jego pula połączeń sprawia, że skrobak Go jest szybki. Nowy klient na każde żądanie wyrzuca keep-alive i wymusza nowy handshake TLS za każdym razem. Trzy pola `Transport` mają znaczenie pod obciążeniem: `MaxIdleConns` i `MaxIdleConnsPerHost` określają rozmiar puli, a `IdleConnTimeout` wycofuje nieaktywne połączenia, więc rotacyjna pula proxy nigdy nie przypina martwego wyjścia. Ustaw je jawnie - domyślne ustawienia biblioteki standardowej są dostrojone do przeglądarki, a nie do skrobaka uruchamiającego tysiące żądań na minutę.

![Tabela błędów proxy w Go z wyróżnioną kolumną przyczyn. Karta przypisuje każdy błąd do przyczyny: proxyconnect lub 407 oznacza złe dane uwierzytelniające, context canceled oznacza, że cancel() wykonało się przed odczytaniem body, EOF oznacza zbyt niskie MaxIdleConnsPerHost, zignorowane HTTP_PROXY oznacza własny Transport, a zawieszenie oznacza brak timeoutu.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335148/blog/generated/qp-b9-009-diag2.png)
*Prawie każdy błąd proxy Go mapuje się na jeden z tych pięciu - sprawdź kolumnę przyczyny, zanim ponownie spróbujesz tego samego wyjścia.*

## Kiedy przestać ręcznie tworzyć transporty

Kod powyżej wystarcza na czyste cele. Gdy strona dodaje Cloudflare, TLS fingerprinting lub renderowanie JavaScript, surowy handshake TLS Go nie wygląda jak ten w Chrome, i żadne proxy nie naprawi tej niezgodności. W tym momencie [Scraper API](https://quantumproxies.io/scraper-api), który posiada prawdziwy odcisk palca przeglądarki, rotuje IP i renderuje JS za ciebie, to mniej kodu i wyższy wskaźnik sukcesu niż utrzymywanie stosu ręcznie. Jeśli wybierasz język dla nowego skrobaka, nasz post na temat [najlepsze proxy do skrobania webowego](https://quantumproxies.io/blog/best-proxies-for-web-scraping) mapuje kompromisy w różnych stosach.

[Przekierowanie renderowania i rotacji do Scraper API](https://quantumproxies.io/scraper-api)

## Często zadawane pytania

### Jak ustawić proxy dla klienta HTTP Go?

Zbuduj `http.Transport` z polem `Proxy` i przekaż go do `http.Client`. Użyj `http.ProxyURL(u)` dla jednego statycznego proxy lub `func(*http.Request) (*url.URL, error)`, aby wybrać wyjście per request. Umieść dane uwierzytelniające w URL jako `http://user:pass@host:port` i zawsze ustaw `Client.Timeout`.

### Dlaczego Go ignoruje moją zmienną HTTP_PROXY?

Ponieważ zbudowałeś niestandardowy `Transport` z jawną funkcją `Proxy`, która zastępuje domyślne zachowanie `ProxyFromEnvironment`. Zmienne środowiskowe mają zastosowanie tylko wtedy, gdy używasz `http.DefaultTransport` lub sam ustawiasz `Proxy: http.ProxyFromEnvironment`. Zauważ również, że żądania do loopback i localhost zawsze omijają proxy.

### Co powoduje 'context canceled' z proxy Go?

Kontekst żądania został anulowany przed odczytaniem ciała odpowiedzi - zazwyczaj wygasły limit czasu per request lub `cancel()`, który uruchomił się zbyt wcześnie przez `defer`. Daj każdemu żądaniu własny kontekst limitu czasu, opróżnij i zamknij ciało przed wykonaniem anulowania, i nie dziel jednego kontekstu anulowalnego między wiele goroutines.

### Czy Go obsługuje SOCKS5 proxy?

Nie w bibliotece standardowej, ale `golang.org/x/net/proxy` dodaje dialer SOCKS5. Utwórz go za pomocą `proxy.SOCKS5` i podłącz do `Transport.DialContext`, aby nazwy hostów rozwiązywały się w proxy, a nie wyciekały przez lokalne wyszukiwanie DNS.

To jest cały obraz po stronie klienta: jedno pole `Transport` do konfiguracji, funkcja do rotacji, `x/net/proxy` dla SOCKS5, `colly` dla uprzejmego skrobania i semafor plus konteksty per request dla współbieżności. Zacznij od czystej rotacyjnej puli, a ominiesz większość listy błędów, zanim się pojawią.

[Zacznij od rezydencyjnych proxy QuantumProxies](https://quantumproxies.io/residential-proxies)
