Fehlgeschlagene Anfragen in Python verwalten

Lernen Sie, fehlgeschlagene HTTP-Anfragen in Python mit effektiven Wiederholungsstrategien und benutzerdefinierter Logik zu verwalten.
8 min lesen
Managing Failed Requests in Python blog image

Wenn Sie mit HTTP arbeiten, sind fehlgeschlagene Anfragen eine unvermeidliche Realität, mit der man umgehen muss. In der Webentwicklung zeigt ein Status 200 eine erfolgreiche Antwort an. Wir erhalten jedoch nicht immer eine 200, und dieser Leitfaden hilft Ihnen zu verstehen, wie Sie mit diesen Nicht-200-Statuscodes umgehen können.

Laut Mozilla lassen sich Statuscodes in folgende Kategorien unterteilen:

  • 100-199: Informationsantworten
  • 200-299: Erfolgreiche Antworten
  • 300-399: Weiterleitungsnachrichten
  • 400-499: Client-Fehlermeldungen
  • 500-599: Server-Fehlermeldungen

Was sind Statuscodes?

Fehlercodes sind wichtig. Beim Erstellen von clientseitigen Programmen wie Web-Scrapern müssen wir uns hauptsächlich auf Statuscodes im Bereich 400+ und 500+ konzentrieren. Codes im 400er-Bereich decken im Allgemeinen Fehler auf der Client-Seite ab, wie Authentifizierungsprobleme, Rate-Limiting, Timeouts und den berüchtigten 404: Datei nicht gefunden-Fehler. Im 500er-Bereich haben wir es in der Regel mit Serverproblemen zu tun.

Mozilla dokumentiert seit Jahrzehnten Webentwicklungsstandards des W3C und der IETF. Nachfolgend finden Sie eine Liste häufiger Fehlercodes, denen Sie begegnen könnten. Diese Liste ist nicht vollständig. Diese Fehler stammen aus Mozillas offizieller Dokumentation. Je nach Zielseite können Ihre Codes leicht abweichen, aber die Logik sollte dieselbe bleiben.

Statuscode Bedeutung Beschreibung
400 Bad Request Überprüfen Sie Ihr Anforderungsformat
401 Unauthorized Überprüfen Sie Ihren API-Schlüssel
403 Forbidden Sie können nicht auf diese Daten zugreifen
404 Not Found Seite/Endpunkt existiert nicht
408 Request Timeout Anfrage abgelaufen, erneut versuchen
429 Too Many Requests Verlangsamen Sie Ihre Anfragen
500 Internal Server Error Allgemeiner Serverfehler, Anfrage wiederholen
501 Not Implemented Server unterstützt dies noch nicht
502 Bad Gateway Fehlgeschlagene Antwort von einem vorgelagerten Server
503 Service Unavailable Server vorübergehend nicht verfügbar, später erneut versuchen
504 Gateway Timeout Timeout beim Warten auf einen vorgelagerten Server

Wiederholungsstrategien

Bei der Implementierung eines Wiederholungsmechanismus können Sie vorgefertigte Bibliotheken wie HTTPAdapter und Tenacity verwenden. Je nach Anwendungsfall möchten Sie möglicherweise auch Ihre eigene Wiederholungslogik schreiben.

Typischerweise benötigen wir ein Wiederholungslimit und eine Backoff-Strategie. Wir brauchen ein Limit, damit wir nicht in einer unendlichen Wiederholungsschleife gefangen werden. Wir müssen schrittweise zurückgehen, um den Host-Server zu respektieren. Wenn Ihre Anfragen zu schnell kommen, werden Sie blockiert oder der Server wird überlastet.

  • Wiederholungslimits: Sie müssen ein Limit setzen. Nach X Wiederholungsversuchen gibt Ihr Scraper auf.
  • Backoff-Algorithmus: Dieser ist relativ einfach. Sie möchten mit einem kleinen Backoff beginnen und ihn bei jeder Wiederholung erhöhen. Wir beginnen mit 0,3, erhöhen dann auf 0,6 und 1,2 und so weiter.

Wir möchten unsere Anfragen bis zu einem bestimmten Limit wiederholen. Nach jeder fehlgeschlagenen Anfrage möchten wir etwas länger warten.

HTTPAdapter

Mit HTTPAdapter müssen wir drei Dinge konfigurieren: total, backoff_factor und status_forcelist. allowed_methods ist keine wirkliche Anforderung, macht unseren Code jedoch sicherer, indem es hilft, unsere Wiederholungsbedingungen zu definieren. Im folgenden Code verwenden wir httpbin, um automatisch einen Fehler zu erzwingen und unsere Wiederholungslogik auszulösen.

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

#create a session
session = requests.Session()

#configure retry settings
retry = Retry(
    total=3,                  #maximum retries
    backoff_factor=0.3,       #time between retries (exponential backoff)
    status_forcelist=(429, 500, 502, 503, 504), #status codes to trigger a retry
    allowed_methods={"GET", "POST"}
)

#mount the adapter with our custom settings
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)

#actually make a request with our retry logic
try:
    print("Making a request with retry logic...")
    response = session.get("https://httpbin.org/status/500")
    response.raise_for_status()
    print("✅ Request successful:", response.status_code)
except requests.exceptions.RequestException as e:
    print("❌ Request failed after retries:", e)

Sobald wir ein Session-Objekt erstellt haben, gehen wir wie folgt vor:

  • Erstellen Sie ein Retry-Objekt und definieren Sie Folgendes:
    • total: Das maximale Limit für die Wiederholung einer Anfrage.
    • backoff_factor: Wartezeit zwischen Wiederholungen. Diese passt sich exponentiell an, wenn unsere Wiederholungen zunehmen.
    • status_forcelist: Eine Liste fehlerhafter Statuscodes. Alle Codes in dieser Liste lösen automatisch eine Wiederholung aus.
  • Erstellen Sie ein HTTPAdapter-Objekt mit unserer retry-Variable: adapter = HTTPAdapter(max_retries=retry).
  • Sobald wir den adapter erstellt haben, binden wir ihn mit session.mount() an die HTTP- und HTTPS-Methoden.

Wenn Sie diesen Code ausführen, werden unsere drei Wiederholungen (total=3) ausgeführt und Sie erhalten die folgende Ausgabe.

Making a request with retry logic...
❌ Request failed after retries: HTTPSConnectionPool(host='httpbin.org', port=443): Max retries exceeded with url: /status/500 (Caused by ResponseError('too many 500 error responses'))

Tenacity

Sie können auch Tenacity verwenden, eine beliebte Open-Source-Wiederholungsbibliothek für Python. Sie ist nicht auf HTTP beschränkt, bietet uns aber eine ausdrucksstarke, verständliche Möglichkeit, Wiederholungen zu implementieren.

Zunächst müssen Sie es installieren.

pip install tenacity

Nach der Installation erstellen wir einen Decorator und verwenden ihn, um eine Requests-Funktion zu umhüllen. Mit unserem @retry-Decorator fügen wir die Argumente stop, wait und retry hinzu.

import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type, RetryError

#define a retry strategy
@retry(
    stop=stop_after_attempt(3),  #retry up to 3 times
    wait=wait_exponential(multiplier=0.3),  #exponential backoff
    retry=retry_if_exception_type(requests.exceptions.RequestException),  #retry on request failures
)

def make_request():
    print("Making a request with retry logic...")
    response = requests.get("https://httpbin.org/status/500")
    response.raise_for_status()
    print("✅ Request successful:", response.status_code)
    return response

# Attempt to make the request
try:
    make_request()
except RetryError as e:
    print("❌ Request failed after all retries:", e)

Die Logik und Einstellungen hier sind unserem ersten Beispiel mit HTTPAdapter sehr ähnlich.

  • stop=stop_after_attempt(3): Dies weist tenacity an, nach 3 fehlgeschlagenen Wiederholungen aufzugeben.
  • wait=wait_exponential(multiplier=0.3) verwendet dasselbe Warten wie zuvor. Es erhöht sich ebenfalls exponentiell, genau wie vorher.
  • retry=retry_if_exception_type(requests.exceptions.RequestException) weist tenacity an, diese Logik bei jeder RequestException anzuwenden.
  • make_request() sendet eine Anfrage an unseren Fehler-Endpunkt. Es erhält alle Eigenschaften des Decorators, den wir darüber erstellt haben.

Wenn Sie diesen Code ausführen, erhalten Sie eine ähnliche Ausgabe.

Making a request with retry logic...
Making a request with retry logic...
Making a request with retry logic...
❌ Request failed after all retries: RetryError[<Future at 0x75e762970760 state=finished raised HTTPError>]

Eigenen Wiederholungsmechanismus erstellen

Sie können auch Ihren eigenen Wiederholungsmechanismus erstellen. Bei benutzerdefiniertem Code kann dies oft der beste Ansatz sein. Mit relativ wenig Code können wir denselben Effekt erzielen, den wir von diesen Bibliotheken erhalten.

Im folgenden Code müssen wir sleep für unser exponentielles Backoff importieren. Wir legen erneut unsere Konfiguration fest: total, backoff_factor und bad_codes. Wir verwenden dann eine while-Schleife, um unsere Wiederholungslogik zu halten. while wir noch Versuche haben und noch nicht erfolgreich waren, versuchen wir die Anfrage.

import requests
from time import sleep

#create a session
session = requests.Session()

#define our retry settings
total = 3
backoff_factor = 0.3
bad_codes = [429, 500, 502, 503, 504]

#try counter and success boolean
current_tries = 0
success = False

#attempt until we succeed or run out of tries
while current_tries < total and not success:
    try:
        print("Making a request with retry logic...")
        response = session.get("https://httpbin.org/status/500")
        if response.status_code in bad_codes:
            raise requests.exceptions.HTTPError(f"Received {response.status_code}, triggering retry")
        print("✅ Request successful:", response.status_code)
        success = True
    except requests.exceptions.RequestException as e:
        print(f"❌ Request failed: {e}, retries left: {total-current_tries}")
        sleep(backoff_factor)
        backoff_factor = backoff_factor * 2
        current_tries+=1

Die eigentliche Logik wird hier durch eine einfache while-Schleife gesteuert.

  • Wenn response.status_code in unserer Liste der bad_codes enthalten ist, lösen wir eine Ausnahme aus.
  • Wenn eine Anfrage fehlschlägt:
    • Geben wir eine Fehlermeldung in der Konsole aus.
    • sleep(backoff_factor) wartet vor dem Senden der nächsten Anfrage.
    • backoff_factor = backoff_factor * 2 verdoppelt unseren backoff_factor für den nächsten Versuch.
    • Wir erhöhen current_tries, damit wir nicht unbegrenzt in der Schleife bleiben.

Hier ist die Ausgabe unserer benutzerdefinierten Wiederholungslogik.

Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 3
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 2
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 1

Blockierungen umgehen

In der Praxis werden einige Seiten Sie blockieren. Es ist Best Practice, immer einen Proxy mit Python-Requests zu verwenden. Mit einem Proxy wird Ihre Anfrage über eine andere Maschine geleitet. Dies schützt Ihre Identität und verhindert, dass Ihre IP-Adresse von Ihrer Zielseite blockiert wird. Wir haben sogar einen detaillierten Leitfaden zum Umgehen von IP-Sperren. Unsere Residential-Proxys sind darauf ausgelegt, diese Herausforderungen zu meistern.

Fazit

Jetzt wissen Sie, wie Sie fehlgeschlagene HTTP-Anfragen in Python behandeln. Ob Sie einen Scraper, einen API-Client oder Automatisierungstools schreiben, Sie wissen, wie Sie mit diesen Problemen umgehen. Um alle Arten von fehlgeschlagenen Anfragen zu vermeiden, haben wir Produkte wie die Web Unlocker API und den Scraping-Browser entwickelt. Diese Tools verarbeiten automatisch Anti-Bot-Maßnahmen, CAPTCHA-Herausforderungen und IP-Sperren und gewährleisten nahtloses und effizientes Web-Scraping selbst für die anspruchsvollsten Websites.

Registrieren Sie sich jetzt und starten Sie noch heute Ihren kostenlosen Test.