Eğer Python ile siteleri parse ediyorsanız veya proxy üzerinden istekleri otomatikleştiriyorsanız, er ya da geç üç
klasik hata ile karşılaşacaksınız: ProxyError,
SSLError: WRONG_VERSION_NUMBER ve HTTP
407 Proxy Authentication Required. Bu yazıda her birinin
nedenini inceleyecek ve düzeltmek için çalışır kod göstereceğiz.
Requests'in proxy ile çalışma şekli: temel sözdizimi
requests kütüphanesi, proxy'leri bir sözlük aracılığıyla
alır proxies, burada anahtarlar istek şemasını
(http ve https)
temsil eder ve değerler, isteğe bağlı kimlik doğrulama ile proxy sunucusunun URL'sidir.
import requests
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
Dikkat edin: HTTPS istekleri için bile, proxy şeması genellikle http://
olarak belirtilir, çünkü çoğu proxy sunucusu HTTP protokolü üzerinden CONNECT tünelini kabul eder, SSL ile sarmalar. Eğer
proxy için https:// belirtirseniz ve bu desteklenmiyorsa —
SSLError alırsınız, bunun analizi aşağıda yapılacaktır.
Eğer SOCKS5 proxy ile çalışıyorsanız, ek bir paket gerekecektir:
pip install requests[socks]
# Ve proxies sözdizimi değişiyor:
proxies = {
"http": "socks5://user:password@proxy_host:port",
"https": "socks5://user:password@proxy_host:port",
}
ProxyError: nedenler ve çözümler
requests.exceptions.ProxyError — proxy sunucusuna
bağlanmaya çalışırken ortaya çıkan düşük seviyeli ağ istisnalarının bir sarmalayıcısıdır. En sık karşılaşılan dört senaryo vardır.
1. Proxy erişilemez veya adres/port yanlış belirtilmiş
requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.',
ConnectionResetError(104, 'Connection reset by peer')))
Host ve portun doğru girildiğinden emin olun ve proxy sunucusunun gerçekten çalıştığını kontrol edin. En hızlı
teşhis yöntemi — curl ile bağlanmayı denemektir:
curl -x http://user:password@proxy_host:port https://httpbin.org/ip -v
2. Proxy isteğinizi engelledi (IP veya User-Agent ile yasaklama)
Veri merkezi proxy'leri, hedef siteler tarafından IP aralıklarının anti-fraud sistemleri tarafından bilindiği için sıkça yasaklanır. Eğer hata belirli bir siteye (örneğin, Wildberries, Ozon veya sosyal medya) erişirken sürekli ortaya çıkıyorsa, tüm sitelere değil — sorun proxy'de değil, onun itibarındadır. Bu durumda, rezidans proxy'lerine geçmek yardımcı olur — bunlar gerçek kullanıcıların IP'lerini kullanır ve anti-fraud sistemleri tarafından daha az yasaklanır.
3. Eş zamanlı bağlantı limiti aşıldı
Eğer çoklu iş parçacığı ile parsing yapıyorsanız ve aynı proxy üzerinden yüzlerce bağlantı açıyorsanız, sunucu
fazla bağlantıları kapatmaya başlayabilir. Çözüm — requests.adapters.HTTPAdapter ile
pool_maxsize ve max_retries sınırlaması kullanmaktır:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries, pool_connections=20, pool_maxsize=20)
session.mount("http://", adapter)
session.mount("https://", adapter)
response = session.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
4. Proxy sunucusunda zaman aşımı
Eğer proxy "asılı" kalırsa ve belirlenen zaman aşımından daha uzun sürerse, ProxyError
alırsınız, bu da ReadTimeoutError ile sarılır. Zaman aşımını makul bir değere
(genellikle 15-30 saniye yeterlidir) artırın ve istisnayı ayrı olarak işleyin:
try:
response = requests.get(url, proxies=proxies, timeout=20)
except requests.exceptions.ProxyError as e:
print(f"Proxy ile ilgili sorun: {e}")
except requests.exceptions.Timeout:
print("Proxy belirlenen süre içinde yanıt vermedi")
SSLError WRONG_VERSION_NUMBER: sorun analizi
Bu hata, ilk bakışta en anlaşılmaz olanlardan biridir, ancak gerçekte neredeyse her zaman tek bir nedeni vardır.
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL:
WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)')))
WRONG_VERSION_NUMBER, istemcinin SSL/TLS el sıkışmasını
gerçekleştirmeye çalıştığı, ancak sunucunun normal HTTP trafiği beklediği anlamına gelir (şifreleme olmadan). En sık neden,
proxies sözlüğünde yanlış belirtilmiş proxy şemasında yatmaktadır.
Sık yapılan hata: proxy sunucusunun adresinde https:// yerine
http:// belirtmektir. Çoğu HTTP(S) proxy, HTTPS trafiğini
CONNECT mekanizması aracılığıyla proxy'lerken bile normal HTTP üzerinden bağlantı kabul eder.
# YANLIŞ — WRONG_VERSION_NUMBER hatasına neden olur
proxies = {
"http": "https://user:password@proxy_host:port",
"https": "https://user:password@proxy_host:port",
}
# DOĞRU
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
İkinci sık neden — fiziksel olarak TLS'yi desteklemeyen bir port. Örneğin, eğer proxy sağlayıcınız sadece HTTP trafiği için 8080 portunu ayırdıysa ve siz doğrudan (CONNECT'i atlayarak) TLS bağlantısı kurmaya çalışıyorsanız, sunucu TLS el sıkışması yerine normal metinle yanıt verecek ve OpenSSL istemci kütüphanesi bu hatayı verecektir. Proxy sağlayıcınızın belgelerinde, her bir portta hangi portların ve protokollerin (HTTP/HTTPS/SOCKS5) mevcut olduğunu kontrol edin.
Üçüncü bir seçenek — socket veya üçüncü taraf
kütüphaneleri kullanarak proxy ile doğrudan TCP bağlantısı kuruyorsunuz, standart requests mekanizmasını atlayarak. Bu durumda,
TLS sarmalamasının yalnızca gerçek hedef site için uygulandığından emin olun, proxy ile bağlantı için değil.
407 Proxy Authentication Required: kimlik doğrulama
407 yanıt kodu — proxy sunucuları için standart 401'in bir eşdeğeridir. Bu, sunucunun kimlik doğrulama talep ettiği anlamına gelir ve siz ya bunu iletmediniz ya da yanlış bir formatta ilettiniz.
Yöntem 1: URL'de doğrudan kullanıcı adı ve şifre
proxies = {
"http": "http://login:password@proxy_host:port",
"https": "http://login:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
Bu yöntem en basit olanıdır, ancak eğer kullanıcı adı veya şifrede özel karakterler varsa (örneğin, @,
:, /),
bunları urllib.parse.quote ile kaçırmanız gerekir:
from urllib.parse import quote
login = quote("user@domain")
password = quote("p@ssw:ord!")
proxy_url = f"http://{login}:{password}@proxy_host:port"
proxies = {"http": proxy_url, "https": proxy_url}
Yöntem 2: Proxy-Authorization başlığını manuel olarak eklemek
Eğer proxy sağlayıcınız standart bir kimlik doğrulama şeması kullanmıyorsa veya daha fazla kontrol istiyorsanız, başlığı manuel olarak base64 kodlaması ile oluşturabilirsiniz:
import base64
import requests
login = "user"
password = "password"
credentials = base64.b64encode(f"{login}:{password}".encode()).decode()
headers = {
"Proxy-Authorization": f"Basic {credentials}"
}
proxies = {
"http": "http://proxy_host:port",
"https": "http://proxy_host:port",
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
headers=headers,
timeout=10
)
Önemli: bazı proxy sağlayıcıları, kimlik doğrulamayı kullanıcı adı/şifreye değil, belirli bir statik IP (whitelist) ile ilişkilendirir. Eğer IP'nizi değiştirirseniz (örneğin, VPN üzerinden çalışıyorsanız veya dinamik IP sağlayıcısı kullanıyorsanız), 407 hatası doğru kimlik bilgileri ile bile ortaya çıkabilir — çünkü mevcut dış IP'niz proxy tarafında beyaz listeye alınmamıştır. Çözüm — sağlayıcının kullanıcı paneline girip izin verilen IP listesini güncellemek veya IP-whitelist yerine kullanıcı adı/şifre ile kimlik doğrulamaya geçmektir.
Retry mantığı ve istisna işleme
Pratikte proxy hataları nadiren tek seferlik olur — özellikle toplu parsing sırasında. Kodun doğru mimarisi, her hatayı ayrı olarak gecikme ile tekrar denemeyi ve kaydetmeyi öngörmelidir.
import time
import requests
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def fetch_with_retry(url, proxies, max_retries=3, backoff=2):
for attempt in range(1, max_retries + 1):
try:
response = requests.get(url, proxies=proxies, timeout=15)
response.raise_for_status()
return response
except requests.exceptions.ProxyError as e:
logger.warning(f"[Deneme {attempt}] ProxyError: {e}")
except requests.exceptions.SSLError as e:
logger.warning(f"[Deneme {attempt}] SSLError: {e}")
except requests.exceptions.HTTPError as e:
if response.status_code == 407:
logger.error("407: kullanıcı adı/şifreyi veya proxy IP whitelist'ini kontrol edin")
break
logger.warning(f"[Deneme {attempt}] HTTPError: {e}")
except requests.exceptions.Timeout:
logger.warning(f"[Deneme {attempt}] Bağlantı zaman aşımı")
time.sleep(backoff * attempt)
raise RuntimeError(f"{url} alınamadı, {max_retries} denemeden sonra")
Bu yaklaşım, hata ayıklama saatlerini tasarruf ettirir: scriptiniz ilk ağ sorununda çökme yerine, her denemede belirli bir hata türünü gösteren anlaşılır bir log alırsınız.
Proxy döngüsü ve yasaklarla mücadele
Eğer ProxyError ve 407 genellikle kod ayarları ile ilgiliyse, toplu yasaklamalar ve belirli alan adlarında sürekli bağlantı reddetmeleri neredeyse her zaman IP havuzunun kalitesi ile ilgilidir. Marketplace'lerde (Wildberries, Ozon, Avito) veya sosyal medya ile çalışırken, her istek için veya belirli bir aralıkta IP döngüsü önerilir.
import random
proxy_pool = [
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
]
def get_random_proxy():
proxy = random.choice(proxy_pool)
return {"http": proxy, "https": proxy}
response = requests.get(url, proxies=get_random_proxy(), timeout=15)
Üretim ortamındaki parserlar için genellikle sağlayıcı tarafında otomatik döngü ile proxy almak daha uygundur — bu durumda IP listesini kendiniz saklamanıza gerek kalmaz, döngü şeffaf bir şekilde tek bir endpoint üzerinden gerçekleşir. Bu durumda rezidans proxy'leri özellikle sert bot koruma sistemlerini aşmak için iyi bir seçimdir, ayrıca veri merkezi proxy'leri ise istek hızının önemli olduğu durumlarda iyi bir seçimdir, hedef site veri merkezi aralıklarını agresif bir şekilde engellemiyorsa.
| Proxy türü | Hız | Yasaklama riski | Görevler |
|---|---|---|---|
| Veri merkezi | Yüksek | Orta-yüksek | Açık API'leri hızlı bir şekilde parse etme |
| Rezidans | Orta | Düşük | Marketplace'leri, sosyal medyayı parse etme |
| Mobil | Orta-düşük | Minimum | Sert bot koruma sistemleri ile çalışma |
Session, zaman aşımı ve SSL doğrulaması
Uzun ömürlü scriptler (örneğin, döngüsel parserlar) için, ayrı çağrılar yerine
requests.Session() kullanmak mantıklıdır. Session, TCP bağlantılarını
yeniden kullanır, bu da proxy sunucusuna olan yükü azaltır ve büyük miktarda isteklerde ProxyError sayısını düşürür.
session = requests.Session()
session.proxies.update({
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
})
for url in urls_list:
try:
resp = session.get(url, timeout=15)
print(resp.status_code, url)
except requests.exceptions.RequestException as e:
print(f"{url} üzerinde hata: {e}")
Ayrıca verify parametresini ayrı olarak belirtmek önemlidir. Eğer
proxy, SSL sertifikasını kendisi değiştiriyorsa (debug için MITM proxy veya bazı kurumsal proxy'ler),
requests sertifika uyuşmazlığı nedeniyle hata verebilir. Doğrulamayı tamamen devre dışı bırakmak
(verify=False) yalnızca test ortamında yapılmalıdır — üretim ortamında
bu, "adam ortada" saldırıları riski oluşturur:
# Sadece yerel hata ayıklama için!
response = requests.get(url, proxies=proxies, verify=False, timeout=15)
# Doğru yöntem — proxy'nin güvenilir sertifikasının yolunu belirtmek
response = requests.get(url, proxies=proxies, verify="/path/to/proxy_cert.pem")
Hata teşhis kontrol listesi
Eğer script proxy hatası ile çöküyorsa, bu listeyi yukarıdan aşağıya kontrol edin — %90 oranında sorun ilk üç maddede bulunacaktır.
proxiessözlüğündeki şemayı kontrol edin — neredeyse her zamanhttp://olmalıdır, hatta HTTPS istekleri için bile.- Proxy'yi doğrudan
curl -xile test edin, böylece kod tarafındaki sorunu dışlayın. - Kullanıcı adı ve şifrenin URL'de kaçırılmamış özel karakterler içermediğinden emin olun.
- Proxy'nin belirli bir IP'ye (whitelist) bağlı olmadığını, kullanıcı adı/şifre ile kimlik doğrulama yapıldığını kontrol edin.
- Proxy'nin çalışmadığını düşünmeden önce
timeout'u 15-30 saniyeye çıkarın. - Eğer hata yalnızca belirli bir sitede çıkıyorsa — sorun IP'nin itibarıdır, kodda değil.
- Tek seferlik denemeler yerine, eksponansiyel gecikme ile retry mantığı ekleyin.
- Toplu parsing yaparken, tüm istekler için tek bir statik proxy yerine IP döngüsü kullanın.
Sonuç
Python requests'ta çoğu proxy hatası üç şeye dayanır: proxy URL'sindeki yanlış şema
(http yerine https),
kimlik doğrulamanın eksikliği veya yanlış formatı ve IP havuzunun kalitesi. ProxyError genellikle
bağlantı sorunu veya IP yasaklaması hakkında bilgi verir, SSLError WRONG_VERSION_NUMBER neredeyse her zaman
yanlış proxy şemasını gösterir, ve 407 — kullanıcı adı/şifre veya whitelist ile ilgili bir sorunu işaret eder.
Eğer marketplace'lerde veya sosyal medyada parsing yaparken yasaklar ve 407 hataları ile sıkça karşılaşıyorsanız, yalnızca kodu değil, proxy kaynağını da gözden geçirmeniz gerekir. Düşük yasak oranı ile stabil bir parsing için rezidans proxy'lerini denemenizi öneririz — bunlar gerçek IP adreslerini kullanır ve korumalı sitelerde ProxyError alma olasılığı çok daha düşüktür. Eğer önceliğiniz hız ve istek hacmi ise, karmaşık bot koruma sistemlerini aşmak yerine, veri merkezi proxy'lerine göz atın — bunlar daha ucuz ve basit parsing görevleri için daha hızlıdır.