Python'da HTTP isteği göndermek için üç kütüphane öne çıkar: Requests, HTTPX ve AIOHTTP. Üçü de aynı temel işi yapar, ama aralarındaki fark küçük bir betik yazarken değil, binlerce isteği aynı anda yönetmeniz gerektiğinde ortaya çıkar. Bu yazıda üç kütüphaneyi kullanım kolaylığı, eşzamanlılık, proxy desteği, hata yönetimi ve ekosistem açısından karşılaştırıyor, her biri için çalışan örnekler veriyor ve Requests'ten asenkrona geçişin yolunu gösteriyoruz.
Örneklerdeki kodlar Python 3.13 ile Requests 2.32, HTTPX 0.28 ve AIOHTTP 3.13 sürümlerinde denenmiştir.
Kısaca üç kütüphane
- Requests: Python'un en yaygın HTTP kütüphanesi. Senkron çalışır, öğrenmesi çok kolaydır ve belgelerle örnekler her yerdedir. Her istek bitene kadar programın o satırda beklediğini unutmayın.
- HTTPX: Requests'e çok benzeyen bir arayüzü hem senkron hem asenkron olarak sunar. HTTP/2 desteği de vardır. Mevcut Requests kodunu asenkrona taşımanın en az zahmetli yolu genellikle HTTPX'tir.
- AIOHTTP: Baştan asenkron tasarlanmış bir kütüphane. İstemcinin yanında bir web sunucusu da içerir. Çok yüksek eşzamanlılık gereken işlerde olgun ve yaygın bir tercihtir.
Senkron ve asenkron ne demek?
Karşılaştırmanın merkezindeki kavram bu olduğu için kısaca açıklayalım. Senkron bir istemci, isteği gönderir ve yanıt gelene kadar bekler; bu sırada program başka hiçbir şey yapmaz. Asenkron bir istemci ise isteği gönderdikten sonra kontrolü olay döngüsüne bırakır; yanıt beklenirken başka istekler yola çıkabilir.
Bir web isteğinin süresinin büyük kısmı ağda, yani hedef sunucunun yanıt vermesini beklerken geçer. Senkron kodda bu bekleme boşa gider; asenkron kodda aynı süre içinde onlarca başka istek beklenebilir. Fark, yüz sayfayı çekerken hissedilmez; on bin sayfayı çekerken saatler ile dakikalar arasındaki farka dönüşür.
Bu üstünlüğün bedeli karmaşıklıktır: async/await sözdizimi, olay döngüsü ve asenkron olmayan kütüphanelerle uyumsuzluk. Bu yüzden "asenkron her zaman daha iyi" demek yanlıştır; ihtiyaç yoksa senkron kod daha okunaklı ve bakımı daha kolaydır.
Temel farklar tek tabloda
| Özellik | Requests | HTTPX | AIOHTTP |
|---|---|---|---|
| Senkron kullanım | Var | Var | Yok |
| Asenkron kullanım | Yok | Var | Var |
| HTTP/2 | Yok | Var (ek paketle) | Yok |
| Proxy (HTTP/HTTPS) | Yerleşik | Yerleşik | Yerleşik |
| SOCKS proxy | requests[socks] ile | httpx[socks] ile | Harici paketle |
| Varsayılan zaman aşımı | Yok (sonsuz bekler) | 5 saniye | 5 dakika (toplam) |
| Yönlendirmeleri izleme | Varsayılan açık | Varsayılan kapalı | Varsayılan açık |
| Bağlantı havuzu | Session ile | Client ile | ClientSession ile |
| Öğrenme eğrisi | Çok kolay | Kolay | Orta |
| En uygun iş | Basit betik, küçük hacim | Senkron + asenkron karışık projeler | Yüksek eşzamanlılık |
Tablodaki "varsayılan zaman aşımı" satırı, üç kütüphane arasındaki en sık gözden kaçan farktır. Requests'te zaman aşımı vermezseniz istek sonsuza kadar bekleyebilir; bu yüzden her Requests çağrısına timeout= eklemek bir alışkanlık olmalıdır.
Requests: basit ve senkron
Kurulum:
pip install requestsProxy üzerinden tek bir istek:
import requests
PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
proxies = {"http": PROXY, "https": PROXY}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=20)
print(response.status_code, response.json())proxies sözlüğündeki https anahtarı, HTTPS adreslere giderken kullanılacak proxy'dir. Değerin http:// ile başlaması doğrudur: HTTPS trafiği proxy ile kurulan bağlantının içinde tünellenir.
Birden fazla istek atacaksanız Session kullanın; bağlantılar yeniden kullanılır, çerezler korunur ve proxy ayarı bir kez yapılır:
import requests
PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
with requests.Session() as session:
session.proxies.update({"http": PROXY, "https": PROXY})
session.headers.update({"User-Agent": "veri-toplama-botu/1.0"})
for path in ["ip", "headers", "user-agent"]:
r = session.get(f"https://httpbin.org/{path}", timeout=20)
print(path, r.status_code)Requests'in sınırı eşzamanlılıktır. 1.000 sayfayı sırayla çekerseniz toplam süre, her isteğin süresinin toplamına yaklaşır. Bunu concurrent.futures.ThreadPoolExecutor ile hafifletmek mümkündür, ancak çok yüksek hacimlerde asenkron bir kütüphane daha verimli çalışır.
HTTPX: iki dünyanın arası
Kurulum:
pip install httpxSenkron kullanım Requests'e neredeyse birebir benzer:
import httpx
PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
with httpx.Client(proxy=PROXY, timeout=20) as client:
response = client.get("https://httpbin.org/ip")
print(response.json())Aynı kodun asenkron hali, birden fazla isteği aynı anda gönderir:
import asyncio
import httpx
PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
URLS = ["https://httpbin.org/ip"] * 10
async def main():
async with httpx.AsyncClient(proxy=PROXY, timeout=20) as client:
responses = await asyncio.gather(*(client.get(url) for url in URLS))
print([r.status_code for r in responses])
asyncio.run(main())Burada 10 istek sırayla değil, aynı anda yola çıkar. Toplam süre en yavaş isteğin süresine yaklaşır.
HTTP/2 kullanmak için pip install "httpx[http2]" kurup istemciye http2=True geçmeniz yeterlidir. Aynı sunucuya çok sayıda istek atarken tek bağlantı üzerinden çoklama yapıldığı için bağlantı kurma maliyeti düşer. Proxy kullanırken HTTP/2, CONNECT tünelinin içinde sizin ile hedef site arasında çalışır; proxy'nin HTTP/2 desteklemesi gerekmez.
AIOHTTP: yüksek eşzamanlılık için
Kurulum:
pip install aiohttpimport asyncio
import aiohttp
PROXY = "http://pr.proxynet.io:8000"
AUTH = aiohttp.BasicAuth("kullanici", "parola")
URLS = ["https://httpbin.org/ip"] * 10
async def fetch(session, url):
async with session.get(url, proxy=PROXY, proxy_auth=AUTH) as response:
return response.status
async def main():
async with aiohttp.ClientSession() as session:
statuses = await asyncio.gather(*(fetch(session, url) for url in URLS))
print(statuses)
asyncio.run(main())AIOHTTP'de proxy, oturum yerine her istekte proxy= parametresiyle verilir. Kimlik bilgilerini proxy_auth ile ayrı geçebileceğiniz gibi adresin içine de (http://kullanici:parola@...) yazabilirsiniz. Her istekte farklı proxy vermek bu tasarımla doğal biçimde mümkündür; kendi IP listenizi döndürmek istiyorsanız AIOHTTP bunu kolaylaştırır.
AIOHTTP'nin bir başka farkı, yanıt gövdesinin bağlam yöneticisi içinde okunması gerektiğidir. async with bloğundan çıktıktan sonra await response.text() çağırmak hata verir; gövdeyi blok içinde okuyup değişkene alın.
Eşzamanlılığı sınırlamak
Asenkron kullanırken eşzamanlılığı sınırsız bırakmayın. On bin URL'yi asyncio.gather ile tek seferde başlatmak hem kendi sisteminizin dosya tanıtıcı sınırını zorlar hem de hedef siteyi anında hız sınırına iter. asyncio.Semaphore ile aynı anda açık istek sayısını sınırlamak iki tarafı da korur:
import asyncio
import httpx
PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
URLS = [f"https://httpbin.org/get?i={i}" for i in range(100)]
LIMIT = asyncio.Semaphore(10)
async def fetch(client, url):
async with LIMIT:
r = await client.get(url)
return r.status_code
async def main():
async with httpx.AsyncClient(proxy=PROXY, timeout=20) as client:
results = await asyncio.gather(*(fetch(client, u) for u in URLS))
print(sum(1 for s in results if s == 200), "başarılı")
asyncio.run(main())Bu örnekte yüz istek kuyruğa alınır, ancak aynı anda en fazla on tanesi açık olur. Sınırı hedef sitenin toleransına ve proxy paketinizin eşzamanlı bağlantı hakkına göre ayarlayın.
Hata yönetimi ve yeniden deneme
Üç kütüphanede de ağ hataları istisna olarak gelir; ancak HTTP hata kodları (404, 429, 500) varsayılan olarak istisna değildir. Yanıtı kontrol etmek sizin işinizdir:
- Requests ve HTTPX'te
response.raise_for_status()çağrısı 4xx ve 5xx yanıtlarda istisna fırlatır. - AIOHTTP'de aynı iş
response.raise_for_status()ile ya da oturum açılırkenraise_for_status=Trueile yapılır.
Proxy ile çalışırken en sık göreceğiniz kodlar şunlardır:
| Kod | Anlamı | Ne yapmalı? |
|---|---|---|
| 407 | Proxy kimlik doğrulaması başarısız | Kullanıcı adı, parola ve özel karakter kodlamasını kontrol edin |
| 429 | Hedef site hız sınırı uyguluyor | Eşzamanlılığı düşürün, bekleyip yeniden deneyin, IP dağıtımını artırın |
| 403 | Hedef site isteği reddediyor | User-Agent ve IP türünü gözden geçirin |
| 502 / 504 | Proxy hedefe ulaşamadı | Farklı bir çıkış IP'siyle yeniden deneyin |
Yeniden deneme mantığını üstel bekleme ile yazın: ilk denemede bir saniye, sonra iki, sonra dört. Sabit aralıklarla ısrar etmek 429 alan bir IP'nin tamamen engellenmesiyle sonuçlanabilir.
Performans: hangisi daha hızlı?
Kütüphane seçiminde "hangisi daha hızlı" sorusu genellikle yanlış yere bakar. Bir web isteğinin süresinin büyük kısmı ağda geçer; kütüphanenin kendi işlem süresi bunun yanında küçüktür.
Belirleyici olan aynı anda kaç isteği bekleyebildiğinizdir:
- Senkron bir istemci her isteği sırayla bekler.
- Asenkron bir istemci yüzlerce isteği aynı anda bekleyebilir.
Yani 10 sayfalık bir iş için Requests yeterlidir. 10.000 sayfalık bir iş için HTTPX'in asenkron istemcisi veya AIOHTTP süreyi ciddi biçimde kısaltır. Aynı sınıf içinde (asenkron HTTPX ile AIOHTTP arasında) ölçülebilir farklar vardır ve AIOHTTP ham verimde genellikle önde gelir; ancak bu fark çoğu projede hedef sitenin yanıt süresinin ve proxy gecikmesinin gölgesinde kalır.
Proxy ve eşzamanlılık birlikte nasıl düşünülmeli?
Asenkron kütüphaneler çok sayıda isteği aynı anda gönderebilir; ama bu isteklerin hepsi tek bir IP'den çıkıyorsa hedef site kısa sürede hız sınırı uygular. Eşzamanlılığı artırdıkça IP dağıtımını da düşünmeniz gerekir:
- Her istekte farklı IP: Rotating Proxy ile tek bir giriş adresi kullanıp her istekte farklı bir çıkış IP'si alırsınız. Kodunuzda değişiklik gerekmez; yukarıdaki örneklerin hepsi olduğu gibi çalışır.
- Oturum boyunca aynı IP: Giriş yapılan veya sepet gibi durum tutan akışlarda Sticky Proxy aynı IP'yi belirli bir süre korur.
- Kendi listenizi döndürmek: Elinizde sabit bir IP listesi varsa rotasyonu kodda yapabilirsiniz; adım adım örnek için Python'da Proxy'ler Nasıl Döndürülür? yazımıza bakın.
- IP türü: Korumalı hedeflerde eşzamanlılıktan önce IP türü belirleyicidir; Residential ve Datacenter Proxy Farkı yazımızda nedenini anlattık.
Requests'ten HTTPX'e geçiş
Mevcut bir Requests projesini asenkrona taşımak istiyorsanız HTTPX en kısa yoldur. Geçişte dikkat edilecek farklar:
| Requests | HTTPX | Not |
|---|---|---|
requests.get(url) | httpx.get(url) | Aynı |
proxies={"http": p, "https": p} | proxy=p | Tek parametre |
timeout=None varsayılan | timeout=5 varsayılan | HTTPX'te zaman aşımı açık gelir |
| Yönlendirme varsayılan izlenir | follow_redirects=True gerekir | HTTPX varsayılan izlemez |
Session() | Client() / AsyncClient() | Aynı mantık |
response.json() | response.json() | Aynı |
Kodun büyük bölümü değişmeden çalışır; farklar yukarıdaki beş satırda toplanır. Önce senkron Client ile geçip testleri çalıştırmak, sonra AsyncClient'a geçmek riski azaltır.
Hangisini seçmelisiniz?
- Requests seçin: Küçük betikler, otomasyon görevleri, API entegrasyonları ve eşzamanlılığın önemli olmadığı işler için.
- HTTPX seçin: Bugün senkron başlayıp yarın asenkrona geçme ihtimaliniz varsa, HTTP/2 gerekiyorsa veya aynı projede iki kullanımı birlikte istiyorsanız.
- AIOHTTP seçin: Baştan asenkron tasarlanmış, çok yüksek hacimli veri toplama işleri ve aynı uygulamada hem istemci hem sunucu ihtiyacı için.
Sayfaları yalnızca HTTP ile çekmek yetmiyorsa, yani içerik JavaScript ile yükleniyorsa, bu kütüphaneler tek başına yeterli olmaz ve bir tarayıcı otomasyonuna ihtiyaç duyarsınız. Bu ayrımı Web Kazıma: JavaScript mi Python mu? yazımızda ele aldık. Python tarafındaki tarayıcı otomasyonu için Selenium ve SeleniumBase ile Proxy rehberlerimize bakabilirsiniz.
Sık sorulan sorular
Requests'ten HTTPX'e geçmek zor mu?
Genellikle hayır. requests.get yerine httpx.get yazmak çoğu basit kodda doğrudan çalışır. Farklar; zaman aşımının varsayılan olarak açık olması, yönlendirmelerin varsayılan olarak takip edilmemesi ve proxy parametresinin adı gibi ayrıntılardadır. Yukarıdaki geçiş tablosu bu farkları listeler.
SOCKS5 proxy nasıl kullanılır?
Requests için pip install "requests[socks]", HTTPX için pip install "httpx[socks]" kurun ve proxy adresinde socks5:// şemasını kullanın. DNS çözümlemesinin de proxy tarafında yapılmasını istiyorsanız Requests'te socks5h:// yazın. Protokoller arasındaki farklar için SOCKS ve HTTP Proxy Farkı yazımıza bakabilirsiniz.
Asenkron kod her zaman daha mı iyi?
Hayır. Asenkron kod daha karmaşıktır ve hataları ayıklamak daha zordur. Eşzamanlılıktan gerçekten fayda görmeyecek bir iş için senkron kod daha okunaklı ve bakımı daha kolaydır.
Requests ile thread kullanmak asenkrona alternatif mi?
Orta hacimler için evet. ThreadPoolExecutor ile onlarca isteği paralel çalıştırmak mümkündür ve kod senkron kalır. Yüzlerce eşzamanlı istekte thread'lerin bellek maliyeti artar; o noktada asenkron istemci daha verimlidir.
Proxy kimlik bilgilerini koda yazmak zorunda mıyım?
Hayır. Üç kütüphane de HTTP_PROXY ve HTTPS_PROXY ortam değişkenlerini okur (HTTPX'te trust_env varsayılan açıktır). Kimlik bilgilerini ortam değişkeninde tutmak, kodu depoya gönderirken sızıntı riskini azaltır.
Aynı anda kaç istek açmalıyım?
Tek bir doğru sayı yoktur. Hedef sitenin toleransı, proxy paketinizin eşzamanlı bağlantı hakkı ve kendi makinenizin sınırları belirler. On ile başlayıp 429 yanıtı görmeden artırmak güvenli bir yöntemdir.
Özetle
Üç kütüphane de işini iyi yapar; seçim projenin ölçeğine bağlıdır. Basit işler için Requests, esneklik ve geleceğe hazırlık için HTTPX, yüksek eşzamanlılık için AIOHTTP öne çıkar. Hangi kütüphaneyi seçerseniz seçin zaman aşımı verin, eşzamanlılığı sınırlayın ve yeniden denemeyi üstel bekleme ile yazın. Hacim arttıkça kütüphane kadar IP stratejisi de önem kazanır. Büyük ölçekli veri toplama altyapısı için veri kazıma çözümlerimize göz atabilirsiniz.




