Python'da istek tekrar denemeleri nasıl uygulanır

Yorumlar: 0

Web kazıma, web'den veri çıkarmak için etkili bir yöntemdir. Birçok geliştirici, basit ve etkili olduğu için web kazıma projelerini gerçekleştirmek için Python istek kütüphanesini kullanmayı tercih ediyor. Bununla birlikte, harika olduğu kadar, istek kütüphanesinin sınırlamaları vardır. Web kazımada karşılaşabileceğimiz tipik bir sorun, genellikle dengesiz veri çıkarımına yol açan başarısız isteklerdir. Bu makalede, Python'da istek yeniden denemelerini uygulama sürecinden geçeceğiz, böylece HTTP hatalarını ele alabilir ve web kazıma komut dosyalarınızı kararlı ve güvenilir tutabilirsiniz.

İstekler kütüphanesi ile çalışmaya başlama

Önce ortamımızı ayarlayalım. Python ve seçtiğiniz herhangi bir IDE'nin kurulu olduğundan emin olun. Daha sonra eğer henüz sahip değilseniz requests kütüphanesini yükleyin.

pip install requests

Kurulduktan sonra, Python'un requests modülünü kullanarak example.com adresine bir istek gönderelim. İşte tam da bunu yapan basit bir fonksiyon:

import requests

def send_request(url):
    """
    Belirtilen URL'ye bir HTTP GET isteği gönderir ve yanıt durum kodunu yazdırır.
    
    Parameters:
        url (str): İsteğin gönderileceği URL.
    """
    response = requests.get(url)
    print('Response Status Code: ', response.status_code)

send_request('https://example.com')

Kod çıktısı aşağıda gösterilmiştir:

How to implement request retries in Python.png

HTTP durum kodlarını daha iyi anlamak için daha yakından inceleyelim.

HTTP durum kodlarını anlama

Sunucu bir HTTP isteğine, isteğin sonucunu gösteren bir durum koduyla yanıt verir. İşte hızlı bir özet:

  1. 1xx (Bilgilendirici): İstek alındı ve işlenmeye devam ediyor.
  2. 2xx (Başarılı): İstek alındı, anlaşıldı ve kabul edildi.
    • 200 Tamam: İstek başarılı oldu. Bu HTTP durum kodlarının yeşil ışığıdır.
  3. 3xx (Yeniden Yönlendirme): İsteği tamamlamak için daha fazla işlem yapılması gerekiyor.
  4. 4xx (İstemci Hatası): İstekle ilgili bir hata oluştu, genellikle istemci tarafındaki bir şeyden kaynaklanır.
  5. 5xx (Sunucu Hatası): Sunucu, kendi tarafındaki bir hata nedeniyle geçerli bir isteği yerine getiremedi.
    • 500 Dahili Sunucu Hatası: Sunucu isteği tamamlayamadı. Bu, sunucunun isteği yerine getirmesini engelleyen beklenmedik bir durumla karşılaştığını gösterir. Bu, kırmızı trafik ışığının HTTP durum kodu eşdeğeridir.
    • 504 Ağ Geçidi Zaman Aşımı: Sunucu, yukarı akış sunucusundan zamanında yanıt alamadı. Bu, bekleme odası zaman aşımı trafik ışığının HTTP durum kodu eşdeğeridir.

Örneğimizde, 200 durum kodu https://example.com isteğinin tamamlandığı anlamına gelir. Bu, sunucunun "Burada her şey yolunda, isteğiniz başarılı oldu" deme şeklidir.

Bu durum kodları bot tespitinde ve bot benzeri davranışlar nedeniyle erişimin ne zaman kısıtlandığını göstermede de rol oynayabilir.

Aşağıda, çoğunlukla bot algılama ve kimlik doğrulama sorunları nedeniyle ortaya çıkan HTTP hata kodlarının hızlı bir özeti bulunmaktadır.

  1. 429 çok fazla istek: bu durum kodu, kullanıcının belirli bir süre içinde çok fazla istek gönderdiğini gösterir ("hız sınırlaması"). Botlar önceden tanımlanmış istek sınırlarını aştığında yaygın olarak verilen bir yanıttır.
  2. 403 yasak: sunucu isteği yerine getirmeyi reddettiğinde bu kod döndürülür. Bu, sunucunun User-Agent veya diğer kriterlere dayanarak isteğin bir bottan geldiğinden şüphelenmesi durumunda ortaya çıkabilir.
  3. 401 yetkisiz: erişim botun sahip olmadığı bir kimlik doğrulaması gerektiriyorsa bu durum kullanılabilir.
  4. 503 service unavailable: bazen sunucunun geçici olarak isteği karşılayamadığını belirtmek için kullanılır, bu da otomatik trafik artışları sırasında meydana gelebilir.

Python'da yeniden deneme mekanizması uygulama

Şimdi Python'da requests kütüphanesi ile HTTP GET isteği yapmak için basit bir yeniden deneme mekanizması yazalım. Ağ isteklerinin bazı ağ sorunları veya sunucu aşırı yüklenmesi nedeniyle başarısız olduğu zamanlar vardır. Bu yüzden eğer isteğimiz başarısız olursa, bu istekleri tekrar denemeliyiz.

Temel yeniden deneme mekanizması

send_request_with_basic_retry_mechanism işlevi, yalnızca bağlantı hatası gibi bir ağ veya istek istisnasıyla karşılaşıldığında yeniden deneyecek temel bir yeniden deneme mekanizmasıyla belirli bir URL'ye HTTP GET istekleri yapar. İsteği en fazla max_retries kez yeniden deneyecektir. Tüm denemeler böyle bir istisna ile başarısız olursa, son karşılaşılan istisnayı yükseltir.

import requests
import time

def send_request_with_basic_retry_mechanism(url, max_retries=2):
    """
    Temel yeniden deneme mekanizmasına sahip bir URL'ye HTTP GET isteği gönderir.
    
    Parameters:
        url (str): İsteğin gönderileceği URL.
        max_retries (int): İsteğin en fazla kaç kez yeniden deneneceği.

    Raises:
        requests.RequestException: Tüm yeniden denemeler başarısız olursa son istisnayı yükseltir.

    """
    for attempt in range(max_retries):
        try:
            response = requests.get(url)
            print('Response status: ', response.status_code)
            break  # İstek başarılı olursa döngüden çıkın
        except requests.RequestException as error:
            print(f"Attempt {attempt+1} failed:", error)
            if attempt < max_retries - 1:
                print(f"Retrying...")
                time.sleep(delay)  # Yeniden denemeden önce bekleyin
            else:
                print("Max retries exceeded.")
                # Maksimum yeniden denemeye ulaşılırsa son istisnayı yeniden yükseltir
                raise
                send_request_with_basic_retry_mechanism('https://example.com')

Gelişmiş yeniden deneme mekanizması

Şimdi temel yeniden deneme mekanizmasını, kazımaya çalıştığımız web sitesinin engellemeyle sonuçlanabilecek bot algılama mekanizmaları uyguladığı senaryoları ele alacak şekilde uyarlayalım. Bu tür senaryoları ele almak için, yalnızca bot algılama blokları değil, aynı zamanda ağ veya sunucu sorunları nedeniyle de olabileceğinden, isteği özenle birden çok kez yeniden denememiz gerekir.

Aşağıdaki send_request_with_advance_retry_mechanism işlevi, isteğe bağlı yeniden deneme denemeleri ve yeniden deneme gecikmesi ile sağlanan URL'ye bir HTTP GET isteği gönderir. Belirtilen deneme sayısı için isteği birden çok kez göndermeye çalışır ve istek başarıyla yanıt alırsa yanıt durum kodunu yazdırır. İstek işlemi sırasında bir hatayla karşılaşırsa, hata mesajını yazdırır ve yeniden dener. Her deneme arasında belirtilen yeniden deneme gecikmesi kadar bekler. Belirtilen sayıda yeniden deneme denemesinden sonra bile istek başarısız olursa, son karşılaşılan istisnayı yükseltir.

Gecikme parametresi, sunucuyu yakın aralıklarla birden fazla istekle bombardıman etmekten kaçındığı için önemlidir. Bunun yerine, sunucunun isteği işlemek için yeterli zamana sahip olmasını bekler ve sunucunun istekleri bir botun değil bir insanın yaptığını düşünmesini sağlar. Bu nedenle, sunucunun aşırı yüklenmesini veya bot karşıtı mekanizmaları tetikleyebilecek yavaş sunucu yanıtını önlemek için yeniden deneme mekanizması geciktirilmelidir.

import requests
import time

def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1):
    """
    Gelişmiş bir yeniden deneme mekanizması ile belirtilen URL'ye bir HTTP GET isteği gönderir.
    
    Parameters:
        url (str): İsteğin gönderileceği URL.
        max_retries (int): İsteğin yeniden deneneceği maksimum sayı. Varsayılan değer 3'tür.
        delay (int): Yeniden denemeler arasındaki gecikme (saniye cinsinden). Varsayılan değer 1'dir.

    Raises:
        requests.RequestException: Tüm yeniden denemeler başarısız olursa son istisnayı yükseltir.
    """
    for attempt in range(max_retries):
        try:
            response = requests.get(url)
            # 4xx veya 5xx durum kodları için bir istisna yükseltin
            response.raise_for_status()
            print('Response Status Code:', response.status_code)
        except requests.RequestException as e:
            # İstek başarısız olursa hata mesajını ve deneme numarasını yazdır
            print(f"Attempt {attempt+1} failed:", e)
            if attempt < max_retries - 1:
                # Yeniden deneme mesajını yazdırın ve yeniden denemeden önce bekleyin
                print(f"Retrying in {delay} seconds...")
                time.sleep(delay)
            else:
                # Maksimum yeniden deneme sayısı aşılırsa, mesaj yazdır ve istisnayı yeniden yükselt
                print("Max retries exceeded.")
                raise

# Örnek kullanım
send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')

Bu uygulamanın dezavantajları şunlardır:

  • 4xx ve 5xx aralıklarına ait tüm durum kodları yeniden denenir. Ancak 404 (Bulunamadı) durum koduyla sonuçlanan isteklerin yeniden denenmesi gerekmez.
  • Bazı bot algılama hizmetleri 200 (OK) durum koduyla yanıt verebilir, ancak yanıt içeriği farklı olabilir. Bu durum mevcut uygulamada ele alınmamaktadır. İçerik uzunluğu doğrulamasının uygulanması bu sorunu çözebilir.

Düzeltilmiş kod ve dezavantajları ele alan yorumlar burada:

import requests
import time

def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1, min_content_length=10):
    """
    Gelişmiş bir yeniden deneme mekanizması ile belirtilen URL'ye bir HTTP GET isteği gönderir.

    Parameters:
        url (str): İsteğin gönderileceği URL.
        max_retries (int): İsteğin yeniden deneneceği maksimum sayı. Varsayılan değer 3'tür.
        delay (int): Yeniden denemeler arasındaki gecikme (saniye cinsinden). Varsayılan değer 1'dir.
        min_content_length (int): Geçerli kabul edilecek minimum yanıt içeriği uzunluğu. Varsayılan değer 10'dur.

    Raises:
        requests.RequestException: Tüm yeniden denemeler başarısız olursa son istisnayı yükseltir.
    """
    for attempt in range(max_retries):
        try:
            response = requests.get(url)
            # 4xx veya 5xx durum kodları için bir istisna yükseltin
            response.raise_for_status()
            
            # Yanıt durum kodunun 404 olup olmadığını kontrol edin
            if response.status_code == 404:
                print("404 Error: Not Found")
                break  # Exit loop for 404 errors
            
            # Yanıt metninin uzunluğunun belirtilen minimum içerik uzunluğundan az olup olmadığını kontrol edin
            if len(response.text) < min_content_length:
                print("Response text length is less than specified minimum. Retrying...")
                time.sleep(delay)
                continue  # İsteği yeniden deneyin
            
            print('Response Status Code:', response.status_code)
            # Koşullar karşılanırsa, döngüden çıkın
            break
            
        except requests.RequestException as e:
            print(f"Attempt {attempt+1} failed:", e)
            if attempt < max_retries - 1:
                print(f"Retrying in {delay} seconds...")
                time.sleep(delay)
            else:
                print("Max retries exceeded.")
                # Maksimum yeniden denemeye ulaşılırsa son istisnayı yeniden yükseltir
                raise

# Örnek kullanım
send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')

Proxy'lerle belirli HTTP hatalarını ele alma

429 Çok Fazla İstek gibi belirli hatalar için, dönen proxy'ler kullanmak isteklerinizi dağıtmanıza ve hız sınırlamasından kaçınmanıza yardımcı olabilir.

Aşağıdaki kod, proxy kullanımı ile birlikte gelişmiş bir yeniden deneme stratejisi uygular. Bu şekilde, bir Python istekleri yeniden deneme mekanizması uygulayabiliriz. Yüksek kaliteli web kazıma proxy'leri kullanmak da önemlidir. Bu proxy'ler proxy rotasyonu için iyi bir algoritmaya ve güvenilir bir havuza sahip olmalıdır.

import requests
import time

def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1, min_content_length=10):
    """
    Gelişmiş bir yeniden deneme mekanizması ile belirtilen URL'ye bir HTTP GET isteği gönderir.

    Parameters:
        url (str): İsteğin gönderileceği URL.
        max_retries (int): İsteğin yeniden deneneceği maksimum sayı. Varsayılan değer 3'tür.
        delay (int): Yeniden denemeler arasındaki gecikme (saniye cinsinden). Varsayılan değer 1'dir.
   
    Raises:
        requests.RequestException: Tüm yeniden denemeler başarısız olursa son istisnayı yükseltir.
    """
    
    proxies = {
        "http": "http://USER:PASS@HOST:PORT",
        "https": "https://USER:PASS@HOST:PORT"
    }
    
    for attempt in range(max_retries):
        try:
            response = requests.get(url, proxies=proxies, verify=False)
            # 4xx veya 5xx durum kodları için bir istisna yükseltin
            response.raise_for_status()
            
            # Yanıt durum kodunun 404 olup olmadığını kontrol edin
            if response.status_code == 404:
                print("404 Error: Not Found")
                break  # 404 hataları için döngüden çık
            
            # Yanıt metninin uzunluğunun 10 karakterden az olup olmadığını kontrol edin
            if len(response.text) < min_content_length:
                print("Response text length is less than 10 characters. Retrying...")
                time.sleep(delay)
                continue  # İsteği yeniden deneyin
            
            print('Response Status Code:', response.status_code)
            # Koşullar karşılanırsa, döngüden çıkın
            break
            
        except requests.RequestException as e:
            print(f"Attempt {attempt+1} failed:", e)
            if attempt < max_retries - 1:
                print(f"Retrying in {delay} seconds...")
                time.sleep(delay)
            else:
                print("Max retries exceeded.")
                # Maksimum yeniden denemeye ulaşılırsa son istisnayı yeniden yükseltir
                raise

send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')

Python'da istek tekrar denemeleri etkili web kazıma için çok önemlidir. Yeniden denemeleri yönetmek için tartıştığımız yöntemler, engellemeyi önlemeye ve veri toplamanın verimliliğini ve güvenilirliğini artırmaya yardımcı olabilir. Bu teknikleri uygulamak, web kazıma komut dosyalarınızı daha sağlam ve bot koruma sistemleri tarafından tespit edilmeye daha az duyarlı hale getirecektir.

Yorumlar:

0 yorumlar