# Go Proxy Scraping: http.Transport, Autenticação, Rotação e Concorrência

Em Go, toda a história de proxy vive em um campo: Transport.Proxy. Configure corretamente e você terá rotação por solicitação, SOCKS5 e colly gratuitamente. Configure errado e você terá 'context canceled' às 2 da manhã.

Go torna o uso de proxies enganosamente simples: toda a história do lado do cliente vive em um campo, `http.Transport.Proxy`. Configure corretamente e a rotação por solicitação, SOCKS5 e a integração com `colly` surgem do mesmo design. Configure errado e você encontrará as falhas clássicas de proxy em Go - `context canceled`, esgotamento do pool de conexões e uma variável `HTTP_PROXY` que seu transporte personalizado ignora silenciosamente. Este guia percorre todo o caminho com código executável.

## A linha única: http.Transport com uma URL de proxy

Toda solicitação com proxy em Go passa por um `Transport`. O pacote `net/http` oferece um auxiliar, `http.ProxyURL`, que fixa um proxy estático para todo o cliente. As credenciais vão direto para o userinfo da URL - Go as transforma no cabeçalho `Proxy-Authorization` para você:

```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
}
```

Sempre configure `Client.Timeout`. Go não tem tempo limite padrão em `http.Client`, então uma única saída inativa bloqueará a goroutine para sempre - a principal razão pela qual um scraper Go parece travar é a falta de um tempo limite, não um alvo lento.

## Rotação de proxies com uma função Proxy

`Proxy` não é limitado a uma URL fixa - é uma função `func(*http.Request) (*url.URL, error)` que Go chama uma vez por solicitação. Esse é o seu gancho de rotação. Escolha um endpoint aleatório de um pool e cada solicitação sai por um IP diferente, sem lógica de gerenciamento de lista no seu loop de busca:

```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
}
```

Para a maioria das raspagens, você não deve gerenciar um pool. Aponte a função `Proxy` para um único [gateway residencial rotativo](https://quantumproxies.io/rotating-proxies) e o gateway lhe fornece um IP novo em cada solicitação em mais de 90 milhões de endereços em mais de 200 países - um endpoint, sem lista, sessões persistentes quando um fluxo precisa da mesma saída por alguns minutos.

![Um único http.Client do Go com um único Transport cuja função Proxy roda a cada solicitação, de modo que as solicitações 1, 2 e 3 saem por IPs de saída diferentes para o mesmo destino. O quadro: a função é chamada uma vez por solicitação, escolhe de um pool ou aponta para um único gateway rotativo, e MaxIdleConnsPerHost: 32 mantém as conexões reutilizadas.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335148/blog/generated/qp-b9-009-diag1.png)
*Um Transport, uma função Proxy: a função é executada por solicitação, então um único cliente roda por todo o pool.*

## Autenticação de Proxy em Go

Colocar `USER:PASS@` na URL do proxy é o caminho limpo e funciona tanto para `ProxyURL` quanto para uma função `Proxy` personalizada. Se preferir manter as credenciais fora da URL, defina o cabeçalho no túnel CONNECT você mesmo via `Transport.ProxyConnectHeader`. Se seu provedor usar autenticação por lista branca de IP, elimine completamente as credenciais e autorize o IP do seu servidor no painel - QuantumProxies suporta ambos. Um CONNECT recusado com `407` ou `proxyconnect tcp` quase sempre significa credenciais ausentes ou incorretas, não um bloqueio do lado do alvo.

## A variável de ambiente HTTP_PROXY (e quando Go a ignora)

O `http.DefaultTransport` do Go usa `http.ProxyFromEnvironment`, que lê `HTTP_PROXY`, `HTTPS_PROXY` e `NO_PROXY` (conforme o pacote `golang.org/x/net/http/httpproxy`). Dois comportamentos confundem as pessoas. Primeiro, essas variáveis aceitam uma URL completa ou um `host:port` simples, onde o esquema `http` é assumido. Segundo, solicitações para `localhost` ou um endereço de loopback sempre ignoram o proxy, retornando uma URL nula. O maior problema é o inverso: no momento em que você constrói seu próprio `&http.Transport{Proxy: ...}`, você *substituiu* o comportamento da variável de ambiente - sua função explícita vence e `HTTP_PROXY` é ignorado. Se você quiser ambos, envolva-os você mesmo.

## Proxies SOCKS5 em Go

A biblioteca padrão não tem um cliente SOCKS5, então use `golang.org/x/net/proxy` e faça a discagem através dele. Alimente o dialer em `Transport.DialContext` para que o DNS seja resolvido no proxy, não na sua máquina (o mesmo problema de vazamento de nome de host que `socks5h` resolve em outras pilhas). Se você está pesando os dois esquemas, nossa nota sobre [SOCKS5 vs HTTP proxies](https://quantumproxies.io/blog/socks5-vs-http-proxy-difference) cobre quando cada um vence:

```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}
```

## Proxies em colly

Se você raspa com `colly`, você obtém rotação sem tocar no transporte. O pacote `colly/proxy` oferece um `RoundRobinProxySwitcher` que alterna endpoints por solicitação, e `LimitRule` lida com o ritmo. Esta é a maneira mais rápida de um crawler rotativo e educado em 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,
})
```

## Concorrência feita corretamente (e a armadilha do context-canceled)

A concorrência do Go é a razão pela qual as equipes o escolhem para raspagem, e também de onde vem o erro `context canceled`. Ele dispara quando o contexto de uma solicitação é cancelado antes que o corpo seja totalmente lido - geralmente um tempo limite por solicitação que expirou, ou um `cancel()` que foi executado muito cedo por causa de um `defer` mal colocado. Limite sua fan-out com um semáforo, dê a cada solicitação seu próprio contexto de tempo limite, e leia e feche o corpo antes que o cancelamento dispare:

```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()
```

Mais duas notas de produção. Defina `MaxIdleConnsPerHost` alto o suficiente para que um grande pool reutilize conexões em vez de esgotar descritores de arquivo - a causa usual de `EOF` após algumas centenas de solicitações. E rode a saída em caso de falha em vez de tentar novamente a mesma; um IP residencial inativo não se recupera na próxima tentativa. Para o design mais amplo em milhões de páginas, nosso guia sobre [arquitetura de raspagem em larga escala](https://quantumproxies.io/blog/large-scale-web-scraping-architecture) cobre filas, deduplicação e nivelamento de proxy.

## Reutilize um cliente, ajuste o transporte

Crie o `http.Client` uma vez e compartilhe-o em cada goroutine - é seguro para uso concorrente, e seu pool de conexões é o que torna um scraper Go rápido. Um cliente novo por solicitação descarta o keep-alive e força um novo handshake TLS toda vez. Três campos `Transport` importam sob carga: `MaxIdleConns` e `MaxIdleConnsPerHost` dimensionam o pool, e `IdleConnTimeout` aposenta conexões inativas para que um pool de proxy rotativo nunca fixe uma saída inativa. Defina-os explicitamente - os padrões da biblioteca padrão são ajustados para um navegador, não um crawler executando milhares de solicitações por minuto.

![Uma tabela de erros de proxy em Go com a coluna de causas destacada. O quadro associa cada erro à sua causa: proxyconnect ou 407 significa credenciais ruins, context canceled significa que cancel() rodou antes de o corpo ser lido, EOF significa MaxIdleConnsPerHost baixo demais, um HTTP_PROXY ignorado significa um Transport personalizado; um travamento, falta de timeout.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335148/blog/generated/qp-b9-009-diag2.png)
*Quase todas as falhas de proxy Go mapeiam para um destes cinco - verifique a coluna de causa antes de tentar novamente a mesma saída.*

## Quando parar de criar transports manualmente

O código acima é suficiente para alvos limpos. Uma vez que um site adiciona Cloudflare, impressão digital TLS ou renderização de JavaScript, um handshake TLS bruto do Go não se parece em nada com o do Chrome, e nenhum proxy corrige essa incompatibilidade. Nesse ponto, uma [Scraper API](https://quantumproxies.io/scraper-api) que carrega uma impressão digital de navegador real, roda IPs e renderiza JS para você é menos código e uma taxa de sucesso mais alta do que manter a pilha manualmente. Se você está escolhendo uma linguagem para um novo scraper, nosso post sobre [melhores proxies para raspagem web](https://quantumproxies.io/blog/best-proxies-for-web-scraping) mapeia as trocas entre pilhas.

[Descarregue a renderização e rotação para a Scraper API](https://quantumproxies.io/scraper-api)

## Perguntas frequentes

### Como configuro um proxy para o cliente HTTP do Go?

Construa um `http.Transport` com um campo `Proxy` e passe-o para `http.Client`. Use `http.ProxyURL(u)` para um único proxy estático, ou uma `func(*http.Request) (*url.URL, error)` para escolher a saída por solicitação. Coloque as credenciais na URL como `http://user:pass@host:port` e sempre defina `Client.Timeout`.

### Por que Go ignora minha variável HTTP_PROXY?

Porque você construiu um `Transport` personalizado com uma função `Proxy` explícita, que substitui o comportamento padrão `ProxyFromEnvironment`. As variáveis de ambiente só se aplicam quando você usa `http.DefaultTransport` ou define `Proxy: http.ProxyFromEnvironment` você mesmo. Além disso, observe que solicitações para loopback e localhost sempre ignoram o proxy.

### O que causa 'context canceled' com um proxy Go?

O contexto da solicitação foi cancelado antes que o corpo da resposta fosse lido - tipicamente um tempo limite por solicitação expirado ou um `cancel()` que foi executado muito cedo via `defer`. Dê a cada solicitação seu próprio contexto de tempo limite, esgote e feche o corpo antes que o cancelamento adiado seja executado, e não compartilhe um único contexto cancelável entre muitas goroutines.

### Go suporta proxies SOCKS5?

Não na biblioteca padrão, mas `golang.org/x/net/proxy` adiciona um dialer SOCKS5. Crie-o com `proxy.SOCKS5` e conecte-o em `Transport.DialContext` para que nomes de host sejam resolvidos no proxy em vez de vazar através de uma consulta DNS local.

Essa é toda a imagem do lado do cliente: um campo `Transport` para configuração, uma função para rotação, `x/net/proxy` para SOCKS5, `colly` para raspagem educada, e um semáforo mais contextos por solicitação para concorrência. Comece com um pool rotativo limpo e você evita a maioria da lista de erros antes que eles aconteçam.

[Comece com proxies residenciais QuantumProxies](https://quantumproxies.io/residential-proxies)
