Büyük veri çağında hız para birimidir. requests veya BeautifulSoup gibi kütüphanelere dayanan ve ardışık bir döngüde çalışan geleneksel senkron web kazıma, büyük ölçekli veri çıkarımı için çok yavaştır. İstekler ana iş parçacığını engellediğinden, kazıyıcınız zamanının çoğunu veriyi işlemek yerine ağ G/Ç'sini (I/O) bekleyerek geçirir. İşte burada asyncio ve aiohttp ön plana çıkar; minimum ek yükle binlerce eşzamanlı bağlantıyı yönetmenizi sağlar. Bu kılavuz, sağlam ve üretim seviyesinde asenkron kazıyıcılar oluşturmaya derinlemesine dalıyor.
Neden Asenkron Olmalıyız?
Python'da asenkron programlama, duraklayıp yürütmeye devam edebilen yordamlara (coroutines) dayanır. Hafıza ve bağlam değiştirme açısından ağır olan iş parçacıklarının aksine, yordamlar hafiftir ve olay döngüsü tarafından yönetilir. Web kazıma için bu, programınızın A Sunucusuna bir istek başlatabileceğini, A'nın yanıtını beklerken B Sunucusuna geçebileceğini ve B'nin yanıtını beklerken C Sunucusuna geçebileceği anlamına gelir. Bu G/Ç eşzamanlılığı, toplam yürütme süresini önemli ölçüde azaltır.
Bununla birlikte, ham hız bazı zorlukları beraberinde getirir: yarış durumları, bellek sızıntıları ve API hız sınırlamaları. Üretim hazır bir kazıyıcı, bunları zarifçe ele almalıdır.
Asenkron Ortamı Kurma
İlk olarak, gerekli kütüphanelerin yüklü olduğundan emin olun. asyncio (dahil) ve aiohttp (üçüncü taraf) ihtiyacınız olacak.
pip install aiohttp aiohttp_socks
Asenkron istemci oturumumuzu tanımlayarak başlıyoruz. Bir ClientSession kullanmak, bağlantı havuzlamasına olanak tanır; bu, aynı ana makineye birden fazla istek yaparken performans için kritik öneme sahiptir.
Temel Uygulama: Asenkron Kazıyıcı
Aşağıda, hata yönetimi, zaman aşımı yönetimi ve hız sınırlaması içeren sağlam bir kazıyıcı şablonu bulunmaktadır. Bu yapı, hedef sunucuları aşırı yüklemeyi önler ve geçici ağ hatalarında betiğinizin çökmesini engeller.
import asyncio
import aiohttp
import time
# Eşzamanlılığı sınırlamak ve hız sınırlamalarına saygı duymak için bir yarı sayıcı tanımlayın
# Bu, aynı anda 10'dan fazla bağlantı açmamamızı sağlar
semaphore = asyncio.Semaphore(10)
async def fetch_url(session, url):
async with semaphore:
try:
# Asılı kalan istekleri önlemek için bir zaman aşımı ayarlayın
timeout = aiohttp.ClientTimeout(total=10)
async with session.get(url, timeout=timeout) as response:
if response.status == 200:
return await response.text()
else:
print(f"{url} alınamadı: Durum {response.status}")
return None
except asyncio.TimeoutError:
print(f"{url} zaman aşımı")
return None
except aiohttp.ClientError as e:
print(f"{url} için İstemci hatası: {e}")
return None
async def main():
urls = [
"https://httpbin.org/html",
"https://httpbin.org/json",
"https://httpbin.org/headers",
] * 50 # 150 URL'yi simüle et
start_time = time.time()
# Tüm işlem için tek bir oturum oluştur
async with aiohttp.ClientSession() as session:
# Tüm görevleri eşzamanlı olarak topla
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
end_time = time.time()
print(f"{len(urls)} istek {end_time - start_time:.2f} saniye içinde tamamlandı")
if __name__ == "__main__":
asyncio.run(main())
Üretim İçin Gelişmiş Düşünceler
Yukarıdaki kod çalışsa da, üretim kazıyıcıları daha fazla sofistike özelliklere ihtiyaç duyar. İşte üç kritik iyileştirme:
- Kullanıcı-Ajanı Döndürme: Birçok sunucu varsayılan Python kullanıcı-ajanlarını engeller. Yasaklanmamak için gerçekçi kullanıcı-ajanı dizelerinden oluşan bir havuz kullanın.
- Yeniden Deneme Mantığı:
tenacitygibi kütüphaneler veya yordamınızın içinde özel mantık kullanarak üstel geri çekilme uygulayın. Bu, bir sunucu geçici olarak 503 hatası döndürdüğünde yardımcı olur. - Bellek Yönetimi: Tüm HTML içeriğini bellekte saklamaktan kaçının. İşleme ağır ise, yanıtları doğrudan diske yazın veya
lxmlgibi bir ayrıştırıcı üzerinden artımlı olarak akın.
Sonuç
asyncio ve aiohttp'u ustalıkla kullanmak, web kazımayı darboğazlı, ardışık bir görevden yüksek verimli bir boru hattına dönüştürür. Hız sınırlaması için yarı sayıcıları ve uygun istisna yönetimini kullanarak, hem hızlı hem de sunucu kaynaklarına saygılı ve hatalara dayanıklı kazıyıcılar oluşturursunuz. Daha fazla ölçeklendirdikçe, bu araçları gerçekten büyük ölçekli veri çıkarımı projeleri için Scrapy-Redis veya asenkron işçiler gibi dağıtılmış çerçevelerle entegre etmeyi düşünün.
Senkron kazıyıcılarınızı bugün yeniden düzenlemeye başlayın ve veri çıkarımı verimliliğinizin uçtuğunu izleyin.