Proksiler aracılığıyla timeout hatalarını nasıl düzeltilir
İstek asılı kaldı, betik TimeoutError hatası ile çöktü, veriler alınamadı. Tanıdık bir durum mu? Proksiler aracılığıyla timeout hataları — ayrıştırma ve otomasyon sırasında en sık karşılaşılan sorunlardan biridir. Nedenleri analiz edelim ve somut çözümler sunalım.
Timeout hataları neden oluşur
Timeout — tek bir sorun değil, bir semptom. Tedavi etmeden önce, nedeni anlamalıyız:
Yavaş proksiler sunucusu. Aşırı yüklü bir sunucu veya coğrafi olarak uzak bir proksiler her istekte gecikme ekler. Timeout'unuz 10 saniye ise ve proksiler 12 saniyede yanıt verirse — hata oluşur.
Hedef sitede engelleme. Site, açık bir reddetme yerine şüpheli istekleri kasıtlı olarak "asılı" bırakabilir. Bu botlara karşı bir taktiktir — bağlantıyı sonsuza kadar açık tutmak.
DNS sorunları. Proksiler alanı çözümlemesi gerekir. Proksinin DNS sunucusu yavaş veya erişilemez ise — istek bağlantı aşamasında asılı kalır.
Yanlış timeout ayarları. Tüm şeyler için tek bir genel timeout — sık yapılan bir hata. Connect timeout ve read timeout — farklı şeylerdir ve ayrı ayrı ayarlanmalıdır.
Ağ sorunları. Paket kaybı, proksinin kararsız bağlantısı, yönlendirme sorunları — tüm bunlar timeout'lara neden olur.
Timeout türleri ve ayarlanması
Çoğu HTTP kütüphanesi birkaç timeout türünü destekler. Aralarındaki farkı anlamak — doğru ayarlamanın anahtarıdır.
Connect timeout
Proksiler ve hedef sunucu ile TCP bağlantısı kurma süresi. Proksiler erişilemez veya sunucu yanıt vermiyorsa — bu timeout tetiklenir. Önerilen değer: 5-10 saniye.
Read timeout
Bağlantı kurulduktan sonra veri bekleme süresi. Sunucu bağlandı ama sessiz — read timeout tetiklenir. Normal sayfalar için: 15-30 saniye. Ağır API'ler için: 60+ saniye.
Total timeout
Başından sonuna kadar tüm istek için geçen süre. Asılı bağlantılara karşı sigorta. Genellikle: connect + read + yedek.
Python'da requests kütüphanesi ile ayarlama örneği:
import requests
proxies = {
"http": "http://user:pass@proxy.example.com:8080",
"https": "http://user:pass@proxy.example.com:8080"
}
# Tuple: (connect_timeout, read_timeout)
timeout = (10, 30)
try:
response = requests.get(
"https://target-site.com/api/data",
proxies=proxies,
timeout=timeout
)
except requests.exceptions.ConnectTimeout:
print("Proksiler veya sunucuya bağlanılamadı")
except requests.exceptions.ReadTimeout:
print("Sunucu zamanında veri göndermedi")
aiohttp (asenkron Python) için:
import aiohttp
import asyncio
async def fetch_with_timeout():
timeout = aiohttp.ClientTimeout(
total=60, # Genel timeout
connect=10, # Bağlantı için
sock_read=30 # Veri okuma için
)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(
"https://target-site.com/api/data",
proxy="http://user:pass@proxy.example.com:8080"
) as response:
return await response.text()
Retry-mantığı: doğru yaklaşım
Timeout — her zaman ölümcül bir hata değildir. Çoğu zaman tekrarlanan istek başarılı olur. Ancak retry akıllıca yapılmalıdır.
Üstel gecikme
Sunucuyu duraklamadan tekrarlanan isteklerle bombardıman etmeyin. Üstel backoff kullanın: her sonraki deneme — artan gecikme ile.
import requests
import time
import random
def fetch_with_retry(url, proxies, max_retries=3):
"""Retry ve üstel gecikme ile istek"""
for attempt in range(max_retries):
try:
response = requests.get(
url,
proxies=proxies,
timeout=(10, 30)
)
response.raise_for_status()
return response
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError) as e:
if attempt == max_retries - 1:
raise # Son deneme — hatayı fırlat
# Üstel gecikme: 1s, 2s, 4s...
# + istek dalgalarını oluşturmamak için rastgele jitter
delay = (2 ** attempt) + random.uniform(0, 1)
print(f"Deneme {attempt + 1} başarısız: {e}")
print(f"{delay:.1f} saniye sonra yeniden dene...")
time.sleep(delay)
tenacity kütüphanesi
Production kodu için hazır çözümleri kullanmak daha uygun:
from tenacity import retry, stop_after_attempt, wait_exponential
from tenacity import retry_if_exception_type
import requests
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
retry=retry_if_exception_type((
requests.exceptions.Timeout,
requests.exceptions.ConnectionError
))
)
def fetch_data(url, proxies):
response = requests.get(url, proxies=proxies, timeout=(10, 30))
response.raise_for_status()
return response.json()
Timeout'larda proksinin rotasyonu
Bir proksiler sürekli timeout veriyorsa — sorun onun içinde. Mantıklı çözüm: başka birine geç.
import requests
from collections import deque
from dataclasses import dataclass, field
from typing import Optional
import time
@dataclass
class ProxyManager:
"""Başarısız denemeleri izleyen proksiler yöneticisi"""
proxies: list
max_failures: int = 3
cooldown_seconds: int = 300
_failures: dict = field(default_factory=dict)
_cooldown_until: dict = field(default_factory=dict)
def get_proxy(self) -> Optional[str]:
"""Çalışan proksiler al"""
current_time = time.time()
for proxy in self.proxies:
# Cooldown'daki proksileri atla
if self._cooldown_until.get(proxy, 0) > current_time:
continue
return proxy
return None # Tüm proksiler cooldown'da
def report_failure(self, proxy: str):
"""Başarısız istek hakkında bildir"""
self._failures[proxy] = self._failures.get(proxy, 0) + 1
if self._failures[proxy] >= self.max_failures:
# Proksiler cooldown'a gönder
self._cooldown_until[proxy] = time.time() + self.cooldown_seconds
self._failures[proxy] = 0
print(f"Proksiler {proxy} cooldown'a gönderildi")
def report_success(self, proxy: str):
"""Başarı durumunda hata sayacını sıfırla"""
self._failures[proxy] = 0
def fetch_with_rotation(url, proxy_manager, max_attempts=5):
"""Hatalar durumunda otomatik proksiler değişimi ile istek"""
for attempt in range(max_attempts):
proxy = proxy_manager.get_proxy()
if not proxy:
raise Exception("Kullanılabilir proksiler yok")
proxies = {"http": proxy, "https": proxy}
try:
response = requests.get(url, proxies=proxies, timeout=(10, 30))
response.raise_for_status()
proxy_manager.report_success(proxy)
return response
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
proxy_manager.report_failure(proxy)
print(f"{proxy} üzerinden timeout, başka bir tane dene...")
continue
raise Exception(f"{max_attempts} denemeden sonra veri alınamadı")
Konut proksilerini otomatik rotasyon ile kullanırken bu mantık basitleşir — sağlayıcı her istek veya belirtilen aralıkta IP'yi kendisi değiştirir.
Timeout kontrolü ile asenkron istekler
Toplu ayrıştırma sırasında senkron istekler verimsizdir. Asenkron yaklaşım yüzlerce URL'yi paralel olarak işlemeyi sağlar, ancak timeout'larla dikkatli çalışmayı gerektirir.
import aiohttp
import asyncio
from typing import List, Tuple
async def fetch_one(
session: aiohttp.ClientSession,
url: str,
semaphore: asyncio.Semaphore
) -> Tuple[str, str | None, str | None]:
"""Timeout işleme ile bir URL'nin yüklenmesi"""
async with semaphore: # Paralelliği sınırla
try:
async with session.get(url) as response:
content = await response.text()
return (url, content, None)
except asyncio.TimeoutError:
return (url, None, "timeout")
except aiohttp.ClientError as e:
return (url, None, str(e))
async def fetch_all(
urls: List[str],
proxy: str,
max_concurrent: int = 10
) -> List[Tuple[str, str | None, str | None]]:
"""Timeout ve paralellik kontrolü ile toplu yükleme"""
timeout = aiohttp.ClientTimeout(total=45, connect=10, sock_read=30)
semaphore = asyncio.Semaphore(max_concurrent)
connector = aiohttp.TCPConnector(
limit=max_concurrent,
limit_per_host=5 # Bir ana bilgisayara en fazla 5 bağlantı
)
async with aiohttp.ClientSession(
timeout=timeout,
connector=connector
) as session:
# Tüm istekler için proksiler ayarla
tasks = [
fetch_one(session, url, semaphore)
for url in urls
]
results = await asyncio.gather(*tasks)
# İstatistikler
success = sum(1 for _, content, _ in results if content)
timeouts = sum(1 for _, _, error in results if error == "timeout")
print(f"Başarılı: {success}, Timeout'lar: {timeouts}")
return results
# Kullanım
async def main():
urls = [f"https://example.com/page/{i}" for i in range(100)]
results = await fetch_all(
urls,
proxy="http://user:pass@proxy.example.com:8080",
max_concurrent=10
)
asyncio.run(main())
Önemli: Çok yüksek paralellik ayarlamayın. Bir proksiler üzerinden 50-100 eşzamanlı istek — zaten çoktur. 10-20 ile birkaç proksiler daha iyidir.
Tanı: nedeni nasıl bulunur
Ayarları değiştirmeden önce sorunun kaynağını belirleyin.
Adım 1: Proksileri doğrudan kontrol edin
# Curl ile basit test ve zaman ölçümü
curl -x http://user:pass@proxy:8080 \
-w "Bağlan: %{time_connect}s\nToplam: %{time_total}s\n" \
-o /dev/null -s \
https://httpbin.org/get
Eğer time_connect 5 saniyeden fazla ise — proksiler veya ona kadar olan ağ ile sorun var.
Adım 2: Doğrudan istek ile karşılaştırın
import requests
import time
def measure_request(url, proxies=None):
start = time.time()
try:
r = requests.get(url, proxies=proxies, timeout=30)
elapsed = time.time() - start
return f"TAMAM: {elapsed:.2f}s, durum: {r.status_code}"
except Exception as e:
elapsed = time.time() - start
return f"BAŞARISIZ: {elapsed:.2f}s, hata: {type(e).__name__}"
url = "https://target-site.com"
proxy = {"http": "http://proxy:8080", "https": "http://proxy:8080"}
print("Doğrudan:", measure_request(url))
print("Proksiler aracılığıyla:", measure_request(url, proxy))
Adım 3: Farklı proksiler türlerini kontrol edin
Timeout'lar proksiler türüne bağlı olabilir:
| Proksiler türü | Tipik gecikme | Önerilen timeout |
|---|---|---|
| Veri merkezi | 50-200 ms | Bağlan: 5s, Oku: 15s |
| Konut | 200-800 ms | Bağlan: 10s, Oku: 30s |
| Mobil | 300-1500 ms | Bağlan: 15s, Oku: 45s |
Adım 4: Ayrıntıları günlüğe kaydedin
import logging
import requests
from requests.adapters import HTTPAdapter
# Debug-günlüğünü etkinleştir
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)
# Şimdi istekle ilgili tüm aşamaları göreceksiniz:
# - DNS çözümleme
# - Bağlantı kurma
# - İstek gönderme
# - Yanıt alma
Timeout hatalarını çözmek için kontrol listesi
Timeout hataları oluştuğunda eylem algoritması:
- Timeout türünü belirleyin — connect mi yoksa read mi? Bunlar farklı sorunlardır.
- Proksileri ayrı kontrol edin — çalışıyor mu? Gecikme ne kadar?
- Timeout'ları artırın — değerler proksiler türü için çok agresif olabilir.
- Backoff ile retry ekleyin — tek timeout'lar normaldir, önemli olan dayanıklılıktır.
- Rotasyonu ayarlayın — sorunlar durumunda başka proksilere otomatik geçiş yapın.
- Paralelliği sınırlandırın — çok fazla eşzamanlı istek proksileri aşırı yükler.
- Hedef siteyi kontrol edin — muhtemelen isteklerinizi engelliyor veya throttle ediyor.
Sonuç
Proksiler aracılığıyla timeout hataları — çözülebilir bir sorundur. Çoğu durumda proksiler türü için timeout'ları doğru ayarlamak, retry-mantığı eklemek ve hata durumunda rotasyon uygulamak yeterlidir. Yüksek güvenilirlik gereksinimleri olan görevler için otomatik rotasyonlu konut proksilerini kullanın — daha fazla bilgi için proxycove.com'u ziyaret edin.