Eğer bir parselleyici yazıyorsanız, veri toplama otomasyonu yapıyorsanız veya farklı bölgelerden API test ediyorsanız — bir gün IP engelleriyle karşılaşacaksınız. Go, yüksek performanslı ağ araçları yazmak için en popüler dillerden biridir ve burada proxy ayarlamanın kendine özgü yönleri vardır. Bu rehberde, temel yapılandırmadan http.Client ile başlayarak, SOCKS5 desteği ile gelişmiş IP döngüsüne kadar her şeyi ele alacağız.
Go uygulamalarında proxy neden gereklidir
Go, yüksek yük altında çalışan ağ araçları yazmak için de facto standart haline geldi: parselleyiciler, tarayıcılar, fiyat izleme botları, API otomasyon araçları. İşte burada proxy ihtiyacı en sık ortaya çıkıyor — ve burada her şeyi baştan doğru ayarlamak önemlidir.
Proxy olmadan geçilemeyecek temel senaryolar:
- Pazar yerlerini parsellemek — Wildberries, Ozon, AliExpress, bir adresten 50-200 istek sonrası IP'yi engelliyor. IP değiştirilmeden parselleyici durur.
- Coğrafi test — farklı ülkeler ve bölgelerden arama motoru sonuçları, reklamlar veya fiyatların kontrolü.
- Rate-limit aşma — yükü birden fazla IP'ye dağıtmak, dakikada yapılan istek sayısı kısıtlamalarını aşmayı sağlar.
- Anonimlik — dış API'lerle çalışırken gerçek sunucu IP'sini gizlemek.
- CDN ve coğrafi konum testleri — farklı bölgeler için içeriğin doğru bir şekilde sunulup sunulmadığını kontrol etmek.
Go'nun standart kütüphanesi, http.Transport ve http.Client aracılığıyla proxy ile çalışmak için esnek araçlar sağlar. Her yöntemi detaylı bir şekilde inceleyeceğiz.
http.Client üzerinden HTTP proxy temel ayarı
Go'da proxy, HTTP istemcisinin taşıma katmanında http.Transport düzeyinde ayarlanır. Bu, isteklerin hangi proxy üzerinden gideceği üzerinde tam kontrol sağlar ve aynı anda farklı proxy'lerle birden fazla istemci oluşturmanıza olanak tanır.
HTTP proxy ayarının en basit çalışma örneği:
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Proxy sunucusunun adresini belirtiyoruz
proxyURL, err := url.Parse("http://123.45.67.89:8080")
if err != nil {
panic(err)
}
// Proxy ile taşıma oluşturuyoruz
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
// Kendi taşıma ile istemci oluşturuyoruz
client := &http.Client{
Transport: transport,
}
// Proxy üzerinden istek yapıyoruz
resp, err := client.Get("https://httpbin.org/ip")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Bu kodda neler oluyor:
url.Parse— proxy adresini*url.URLyapısına ayrıştırırhttp.Transport{Proxy: http.ProxyURL(proxyURL)}— belirtilen proxy üzerinden tüm istekleri yönlendiren bir taşıma oluştururhttp.Client{Transport: transport}— kendi taşıma ile istemci oluşturur
Önemli bir nokta: Eğer http.DefaultClient kullanıyorsanız (yani açık bir istemci olmadan http.Get() kullanıyorsanız), proxy uygulanmayacaktır. Her zaman proxy ile çalışmak için kendi http.Client oluşturun.
💡 İpucu
http.Client içinde zaman aşımı ekleyin: Timeout: 30 * time.Second. Proxy sunucuları doğrudan bağlantıya göre daha yavaş yanıt verebilir ve zaman aşımı olmadan gorutinler sonsuz bir şekilde bekleyebilir.
Çevresel değişkenler aracılığıyla proxy
Go, proxy için standart çevresel değişkenleri destekler — HTTP_PROXY, HTTPS_PROXY ve NO_PROXY. Standart taşıma http.DefaultTransport, bunları otomatik olarak http.ProxyFromEnvironment fonksiyonu aracılığıyla okur.
Uygulamayı başlatmadan önce çevresel değişkenleri ayarlama:
# Terminalde (Linux/macOS)
export HTTP_PROXY="http://123.45.67.89:8080"
export HTTPS_PROXY="http://123.45.67.89:8080"
export NO_PROXY="localhost,127.0.0.1"
# Uygulamayı başlatıyoruz
go run main.go
Eğer kendi taşımanızda ProxyFromEnvironment kullanmak istiyorsanız (örneğin, ek TLS ayarları ile), şöyle yapabilirsiniz:
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
// Ek ayarlar...
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
}
client := &http.Client{
Transport: transport,
Timeout: 60 * time.Second,
}
Bu yaklaşım, proxy adresinin dağıtım yapılandırması (Docker, Kubernetes, systemd) aracılığıyla belirlendiği üretim ortamları için uygundur ve kodda sabitlenmez. Ayrıca, uygulamayı yeniden derlemeden proxy değiştirmeyi kolaylaştırır.
Golang'da SOCKS5 proxy bağlantısı
SOCKS5, HTTP proxy'ye kıyasla daha evrensel bir protokoldür. Daha düşük bir seviyede çalışır ve yalnızca HTTP/HTTPS değil, her türlü TCP/UDP bağlantısını destekler. Parselleyiciler ve otomasyon için SOCKS5 genellikle tercih edilir — özellikle konut proxy'leri kullanıldığında, bu protokol üzerinden sağlanır.
SOCKS5 ile çalışmak için Go'da golang.org/x/net/proxy paketine ihtiyacınız var:
go get golang.org/x/net/proxy
Kimlik doğrulaması olmadan SOCKS5 proxy bağlantısı örneği:
package main
import (
"fmt"
"io"
"net/http"
"golang.org/x/net/proxy"
)
func main() {
// SOCKS5 dialer oluşturuyoruz
dialer, err := proxy.SOCKS5("tcp", "123.45.67.89:1080", nil, proxy.Direct)
if err != nil {
panic(err)
}
// Özel dialer ile taşıma oluşturuyoruz
transport := &http.Transport{
Dial: dialer.Dial,
}
client := &http.Client{
Transport: transport,
}
resp, err := client.Get("https://httpbin.org/ip")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Go 1.20+ ile birlikte url.Parse ile socks5:// ön eki kullanarak yerleşik SOCKS5 desteği de kullanılabilir:
proxyURL, _ := url.Parse("socks5://123.45.67.89:1080")
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
client := &http.Client{Transport: transport}
Bu sözdizimi daha basittir ve ek bağımlılıklar gerektirmez. Ancak daha karmaşık senaryolar (örneğin, eski Go sürümlerinde kimlik doğrulamalı SOCKS5) için golang.org/x/net/proxy paketi hala geçerlidir.
Kimlik doğrulama: proxy'de kullanıcı adı ve şifre
Çoğu ticari proxy sağlayıcısı, kullanıcı adı ve şifre ile kimlik doğrulaması kullanır. Go'da kimlik bilgilerini iletmenin iki yolu vardır.
Yöntem 1: URL'ye gömülü olarak
// Format: protocol://username:password@host:port
proxyURL, err := url.Parse("http://myuser:[email protected]:8080")
if err != nil {
panic(err)
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
Yöntem 2: url.URL yapısı aracılığıyla (daha güvenli)
proxyURL := &url.URL{
Scheme: "http",
Host: "proxy.example.com:8080",
User: url.UserPassword("myuser", "mypassword"),
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
İkinci yöntem tercih edilir — özel karakterleri (örneğin, @, #, :) doğru bir şekilde işler, bu da ilk seçenekte URL ayrıştırmasını bozabilir.
Kimlik doğrulamalı SOCKS5 için golang.org/x/net/proxy paketi:
auth := &proxy.Auth{
User: "myuser",
Password: "mypassword",
}
dialer, err := proxy.SOCKS5("tcp", "proxy.example.com:1080", auth, proxy.Direct)
if err != nil {
panic(err)
}
transport := &http.Transport{
Dial: dialer.Dial,
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
⚠️ Güvenlik
Proxy kullanıcı adı ve şifrelerini asla kodda sabit olarak yazmayın. Çevresel değişkenler veya depoya girmeyen yapılandırma dosyaları kullanın. .env dosyasını .gitignore dosyasına ekleyin.
Proxy döngüsü: istekler arasında IP değiştirme
Tek bir proxy, test için iyidir, ancak gerçek parselleme için IP döngüsü gereklidir. Siteler, bir adresten gelen isteklerin sıklığını izler ve hatta iyi bir proxy, ardışık binlerce istek gönderildiğinde engellenir.
Go'da döngü için iki yaklaşım vardır:
Yöntem 1: Round-robin ile proxy listesi
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"sync/atomic"
"time"
)
type ProxyRotator struct {
proxies []*url.URL
counter uint64
}
func NewProxyRotator(proxyURLs []string) (*ProxyRotator, error) {
proxies := make([]*url.URL, 0, len(proxyURLs))
for _, p := range proxyURLs {
u, err := url.Parse(p)
if err != nil {
return nil, err
}
proxies = append(proxies, u)
}
return &ProxyRotator{proxies: proxies}, nil
}
// GetClient, sıradaki proxy ile istemci döndürür
func (r *ProxyRotator) GetClient() *http.Client {
idx := atomic.AddUint64(&r.counter, 1) % uint64(len(r.proxies))
proxyURL := r.proxies[idx]
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
return &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
}
func main() {
proxyList := []string{
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
}
rotator, err := NewProxyRotator(proxyList)
if err != nil {
panic(err)
}
// Her istek sıradaki proxy üzerinden geçiyor
for i := 0; i < 10; i++ {
client := rotator.GetClient()
resp, err := client.Get("https://httpbin.org/ip")
if err != nil {
fmt.Printf("İstek %d hatası: %v\n", i, err)
continue
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("İstek %d: %s\n", i, string(body))
}
}
Yöntem 2: Fonksiyon aracılığıyla dinamik proxy seçimi
http.Transport içindeki Proxy alanı, func(*http.Request) (*url.URL, error) imzasına sahip bir fonksiyonu kabul eder. Bu, her istek için dinamik olarak proxy seçmenizi sağlar:
proxies := []*url.URL{
mustParseURL("http://proxy1.example.com:8080"),
mustParseURL("http://proxy2.example.com:8080"),
mustParseURL("http://proxy3.example.com:8080"),
}
var counter uint64
transport := &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
// İsteğe göre proxy seçiyoruz (round-robin)
idx := atomic.AddUint64(&counter, 1) % uint64(len(proxies))
return proxies[idx], nil
},
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
func mustParseURL(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
panic(err)
}
return u
}
İkinci yaklaşım daha şıktır — tek bir istemci, her istek için proxy'yi otomatik olarak döndürür. Bu, gorutinler aracılığıyla eşzamanlı isteklerde özellikle kullanışlıdır.
Go projesi için hangi proxy türünü seçmelisiniz
Proxy türünün seçimi, projenizin başarısını doğrudan etkiler. Farklı görevler için farklı türler uygundur — temel seçenekleri inceleyelim:
| Proxy Türü | Hız | Anonimlik | En iyi hangi amaçlar için |
|---|---|---|---|
| Veri merkezi | ⚡ Yüksek | Orta | Açık veri parselleme, API test etme, yük testi |
| Konut | 🔄 Orta | Yüksek | Pazar yerlerini parselleme (Wildberries, Ozon), anti-bot korumalarını aşma |
| Mobil | 🔄 Orta | Maksimum | Sosyal medya ile çalışma, sert anti-bot korumaları olan hizmetler |
Pazar yerlerini parsellemek için (Wildberries, Ozon, Avito): konut proxy'leri kullanın. Bu platformlar, bot tespit sistemlerini (Cloudflare, kendi çözümleri) aktif bir şekilde kullanır ve veri merkezi IP'lerini kolayca engeller. Konut IP'leri, sıradan ev kullanıcıları gibi görünür.
API test etme ve yük testi için: veri merkezi proxy'leri en iyi seçimdir. Yüksek hız, kararlı bağlantı, öngörülebilir gecikme.
Coğrafi testler için: ülke/şehir seçimi ile konut proxy'leri. Servisinizin veya reklamınızın belirli bir bölgedeki kullanıcılar için nasıl göründüğünü kontrol etmenizi sağlar.
Sık yapılan hatalar ve en iyi uygulamalar
Go projelerinde proxy ile çalışırken yıllar içinde, geliştirmeyi yavaşlatan ve parselleyicilerin güvenilirliğini azaltan tipik hataların bir listesi oluştu. Her birini inceleyelim.
Hata 1: Bağlantıları sıfırlamadan taşımanın tekrar kullanılması
http.Transport TCP bağlantılarını önbelleğe alır (keep-alive). Her istek için yeni bir taşıma oluşturursanız, bu avantajı kaybedersiniz. Ancak, döngü sırasında sabit bir proxy ile bir taşıma kullanıyorsanız — istekler eski bir bağlantı üzerinden gidebilir ve yeni proxy'yi atlayabilir.
Çözüm: Proxy değiştirirken transport.CloseIdleConnections() çağrısını yapın veya döngü ile ilgili bölümde açıklanan dinamik proxy seçimi ile işlevsel bir yaklaşım kullanın.
Hata 2: Proxy hatalarının işlenmemesi
Proxy'ler erişilemez, aşırı yüklenmiş veya hedef site tarafından engellenmiş olabilir. Retry mantığı olmadan parselleyiciniz ilk hatada duracaktır.
func fetchWithRetry(client *http.Client, targetURL string, maxRetries int) ([]byte, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
resp, err := client.Get(targetURL)
if err != nil {
lastErr = err
time.Sleep(time.Duration(i+1) * time.Second) // Üstel gecikme
continue
}
defer resp.Body.Close()
if resp.StatusCode == 429 || resp.StatusCode == 403 {
lastErr = fmt.Errorf("engellendi: durum %d", resp.StatusCode)
time.Sleep(time.Duration(i+1) * 2 * time.Second)
continue
}
return io.ReadAll(resp.Body)
}
return nil, fmt.Errorf("tüm denemeler tükendi: %w", lastErr)
}
Hata 3: TLS doğrulamasının göz ardı edilmesi
Bazı geliştiriciler, proxy için TLS sertifikalarının doğrulamasını devre dışı bırakır (InsecureSkipVerify: true). Bu, MITM saldırıları için bir zafiyet oluşturur. Eğer proxy sunucusu kendinden imzalı bir sertifika kullanıyorsa — onu güvenilirler havuzuna ekleyin, doğrulamayı tamamen devre dışı bırakmayın.
Hata 4: Tüm istekler için aynı başlıkların kullanılması
Proxy döngüsü olsa bile, başlık kalıpları nedeniyle engellenebilirsiniz. Proxy ile birlikte User-Agent'ı değiştirin, gerçekçi tarayıcı başlıkları ekleyin:
userAgents := []string{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/118.0.0.0 Safari/537.36",
}
req, _ := http.NewRequest("GET", targetURL, nil)
req.Header.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Set("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7")
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
Go'da güvenilir bir proxy istemcisi için kontrol listesi
- ✅ İstemci için zaman aşımı (
Timeout: 30 * time.Second) - ✅ Üstel gecikme ile retry mantığı
- ✅ 403/429 hatalarında proxy döngüsü
- ✅ Proxy ile birlikte User-Agent döngüsü
- ✅ Kullanılan proxy ile birlikte hata kaydı
- ✅ Kimlik bilgileri çevresel değişkenler aracılığıyla, sabit kodlama yok
- ✅
resp.Body'yideferile kapatma - ✅ Sınırlı eşzamanlılık için semafor veya
worker poolkullanma
Proxy döngüsü ile tam bir worker pool örneği
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"sync"
"sync/atomic"
"time"
)
type Scraper struct {
proxies []*url.URL
counter uint64
workers int
}
func (s *Scraper) getClient() *http.Client {
idx := atomic.AddUint64(&s.counter, 1) % uint64(len(s.proxies))
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(s.proxies[idx]),
},
Timeout: 30 * time.Second,
}
}
func (s *Scraper) Scrape(urls []string) map[string]string {
results := make(map[string]string)
var mu sync.Mutex
var wg sync.WaitGroup
sem := make(chan struct{}, s.workers) // Eşzamanlılığı sınırlamak için semafor
for _, u := range urls {
wg.Add(1)
go func(targetURL string) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
client := s.getClient()
resp, err := client.Get(targetURL)
if err != nil {
fmt.Printf("Hata: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
mu.Lock()
results[targetURL] = string(body)
mu.Unlock()
}(u)
}
wg.Wait()
return results
}
Sonuç
Go'da proxy ayarlamak, birkaç satır kod ile çözülebilir bir görevdir, ancak http.Transport'ın doğru kullanımı, kimlik doğrulama işleminin düzgün bir şekilde işlenmesi, IP döngüsünün akıllıca yapılması ve hata yönetimi gerektirir. Standart kütüphane, HTTP proxy'leri için gerekli her şeyi sağlar ve golang.org/x/net/proxy paketi, SOCKS5 ihtiyacını karşılar.
Makaleden çıkarılan anahtar noktalar:
- Her zaman kendi
http.Client'ınızı oluşturun —http.DefaultClientdoğrudan proxy'yi desteklemez - Dinamik döngü için işlevsel yaklaşımı kullanın (
Proxy: func(r *Request) (*url.URL, error)) - Kimlik bilgilerini çevresel değişkenlerde saklayın, kodda değil
- Retry mantığı ekleyin — proxy'ler %100 güvenilir değildir
- Proxy ile birlikte User-Agent'ı değiştirin, maksimum verimlilik için
Eğer Wildberries veya Ozon gibi pazar yerleri için bir parselleyici yazıyorsanız, konut proxy'leri kullanmanızı öneririz — bunlar gerçek ev kullanıcılarının IP'lerine sahiptir ve anti-bot sistemleri tarafından engellenme olasılıkları çok daha düşüktür. Dakikada binlerce istekle yüksek yük altında çalışacak senaryolar için en iyi seçim, veri merkezi proxy'leri olacaktır — bunlar maksimum hız ve bağlantı istikrarı sağlar.
```