HTTPX, Requests ve AIOHTTP Karşılaştırması

Yayın tarihi:

9 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
Üç sütun halinde kod blokları

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

ÖzellikRequestsHTTPXAIOHTTP
Senkron kullanımVarVarYok
Asenkron kullanımYokVarVar
HTTP/2YokVar (ek paketle)Yok
Proxy (HTTP/HTTPS)YerleşikYerleşikYerleşik
SOCKS proxyrequests[socks] ilehttpx[socks] ileHarici paketle
Varsayılan zaman aşımıYok (sonsuz bekler)5 saniye5 dakika (toplam)
Yönlendirmeleri izlemeVarsayılan açıkVarsayılan kapalıVarsayılan açık
Bağlantı havuzuSession ileClient ileClientSession ile
Öğrenme eğrisiÇok kolayKolayOrta
En uygun işBasit betik, küçük hacimSenkron + asenkron karışık projelerYü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:

bash
pip install requests

Proxy üzerinden tek bir istek:

python
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:

python
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:

bash
pip install httpx

Senkron kullanım Requests'e neredeyse birebir benzer:

python
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:

python
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:

bash
pip install aiohttp
python
import 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:

python
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ırken raise_for_status=True ile yapılır.

Proxy ile çalışırken en sık göreceğiniz kodlar şunlardır:

KodAnlamıNe yapmalı?
407Proxy kimlik doğrulaması başarısızKullanıcı adı, parola ve özel karakter kodlamasını kontrol edin
429Hedef site hız sınırı uyguluyorEşzamanlılığı düşürün, bekleyip yeniden deneyin, IP dağıtımını artırın
403Hedef site isteği reddediyorUser-Agent ve IP türünü gözden geçirin
502 / 504Proxy 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:

RequestsHTTPXNot
requests.get(url)httpx.get(url)Aynı
proxies={"http": p, "https": p}proxy=pTek parametre
timeout=None varsayılantimeout=5 varsayılanHTTPX'te zaman aşımı açık gelir
Yönlendirme varsayılan izlenirfollow_redirects=True gerekirHTTPX 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.

ChatGPT'ye sorClaude'a sor