Zum Inhalt springen
Zurück zum Blog

Die 6 besten Python-HTTP-Clients für das Web-Scraping

Mihnea-Octavian ManolacheZuletzt aktualisiert am 19 min read
Die 6 besten Python-HTTP-Clients für das Web-Scraping
TL;DR: Wählen Sie „Requests“ für einfache synchrone Skripte, „HTTPX“, wenn Sie sowohl synchrone als auch asynchrone Schnittstellen mit optionalem HTTP/2 wünschen, und „aiohttp“ für einen asyncio-basierten Crawler mit expliziter Verbindungssteuerung. Ziehen Sie „urllib3“ für Pooling und Wiederholungsversuche auf niedrigerer Ebene, „curl_cffi“ für die Nachahmung von Browser-Transfers oder „Niquests“ für eine umfassendere Unterstützung des HTTP-Protokolls in Betracht. Die Wiederverwendung von Sitzungen, die Begrenzung der Parallelität und die Überprüfung der zurückgegebenen Daten sind wichtiger als ein allgemeines Geschwindigkeitsranking.

Ein Python-HTTP-Client sendet Anfragen an einen Server und gewährt Ihrem Code Zugriff auf den Antwortstatus, die Header und den Body. Beim Web-Scraping übernimmt er den Abrufschritt; ein HTML-Parser extrahiert Felder, während ein Browser benötigt wird, wenn der Workflow von der Ausführung von JavaScript oder der Interaktion mit einer Seite abhängt.

Die besten Python-HTTP-Clients für das Web-Scraping lösen unterschiedliche Probleme. Ein kleines, zeitgesteuertes Skript benötigt möglicherweise lesbaren Code und eine zuverlässige Fehlerbehandlung. Ein Crawler, der viele unabhängige Seiten abruft, benötigt möglicherweise asynchrone Anfragen und Gegendruck. Ein Ziel, das ein neueres Protokoll verwendet, kann einen anderen Transport rechtfertigen, aber diese Änderung behebt nicht automatisch fehlende Daten oder abgelehnte Anfragen.

Dieser Leitfaden vergleicht sechs Clients hinsichtlich ihrer Eignung für bestimmte Arbeitslasten und zeigt, wie die drei gängigsten Ausgangspunkte konfiguriert werden. Die Beispiele extrahieren Seitentitel von einer Übungswebsite, verwenden explizite Timeouts und schließen ihre Sitzungen. Sie erfahren außerdem, wo Wiederholungsversuche sinnvoll sind, wie man Clients ohne irreführende Benchmarks vergleicht und wann der Wechsel der Bibliothek die falsche Lösung ist.

Die Empfehlungen basieren auf dokumentierten Funktionen und nicht auf der Behauptung, dass eine bestimmte Bibliothek auf jeder Website die schnellste ist. Überprüfen Sie die von Ihnen gewählte Version und testen Sie sie vor der Bereitstellung mit repräsentativen Antworten.

Die besten Python-HTTP-Clients für Web-Scraping: ein kurzer Vergleich

Beginnen Sie mit dem Ausführungsmodell Ihrer Anwendung. Requests eignet sich für ein sequentielles Skript. HTTPX bietet Ihnen ähnliche Konzepte für Anfragen und Antworten sowohl im synchronen als auch im asynchronen Code. aiohttp passt zu einem System, das die Netzwerkarbeit bereits mit asyncio koordiniert.

Wechseln Sie erst dann zu einem spezialisierten Client, wenn Sie die benötigte Funktion konkret benennen können. Mehr Protokolloptionen oder browserähnliche Transporteinstellungen führen zu einer weiteren Abhängigkeit, die es zu verstehen gilt; sie sind kein Ersatz für die Analyse des Fehlers, den Sie beheben möchten.

Client

Beste Eignung

Anfragemodell

Wichtige Funktion

Wichtigster Kompromiss

Anforderungen

Kleine Skripte und bestehende synchrone Anwendungen

Synchronisation

Vertraute API, Sitzungen, Cookies, adapterbasierte Konfiguration für Wiederholungsversuche

Keine native asyncio-Schnittstelle oder integriertes HTTP/2

HTTPX

Projekte, die synchrone und asynchrone Clients benötigen

Synchron und asynchron

Optionales HTTP/2, explizite Timeout-Kategorien, wiederverwendbare Clients

Ähnlich wie Requests, jedoch nicht verhaltensmäßig identisch

aiohttp

Asyncio-basierte Collection-Pipelines

Asynchron

Sitzungspooling, Verbindungsbeschränkungen pro Host, Streaming und WebSockets

Umfassendere Verwaltung des Lebenszyklus der Ereignisschleife; nicht für HTTP/2 geeignet

urllib3

Benutzerdefinierte Abruf-Infrastruktur

Synchron

Direkte Steuerung von Pool und Wiederholungsrichtlinien

Umfassendere Verwaltung von Antworten und Sitzungen auf Anwendungsebene

curl_cffi

Workloads, die eine Identitätsübernahme beim Browser-Transport erfordern

Synchron und asynchron

Identitätsübernahme für Browser-TLS- und HTTP/2-Profile

Native Transportabhängigkeiten; keine JavaScript-Ausführung

Niquests

Anwendungen, die eine umfassendere Unterstützung des HTTP-Protokolls benötigen

Synchron und asynchron

Unterstützung für HTTP/1.1, HTTP/2 und HTTP/3

Kompatibilität bei Aushandlung und Bereitstellung muss getestet werden

Verwenden Sie diesen Vergleich von Python-HTTP-Clients als Auswahlliste und nicht als Leistungsrangliste. Ein Crawler, der beispielsweise jeweils nur eine Seite abfragt, kann von der Verwendung einer asynchronen Bibliothek nur dann profitieren, wenn sich auch seine Ablaufplanung ändert. Umgekehrt hat ein Programm, das bereits auf viele unabhängige Netzwerkoperationen wartet, einen konkreten Grund, einen asynchronen Python-HTTP-Client in Betracht zu ziehen.

Halten Sie die Grenzen des Scraping-Stacks klar:

Ebene

Seine Zuständigkeit

Beispiel für eine Entscheidung

Zeitplanung

Entscheidung, welche URL wann abgerufen werden soll

Gebundene Aufgaben in der Warteschlange zuordnen und das Tempo für jede Quelle festlegen

HTTP-Client

Senden Sie die Anfrage und empfangen Sie die Antwort

Wählen Sie eine Sitzung, einen Transport und eine Timeout-Richtlinie aus

Parsen

HTML oder JSON in Felder umwandeln

Titel, Preis oder erforderliche Kennung validieren

Speicherung

Speichern Sie gültige Ergebnisse und Fehler

Datensätze deduplizieren und sichere Wiederholungen ermöglichen

Diese Trennung sorgt für geringere zukünftige Änderungen. Wenn das Abrufen das korrekte HTML zurückgibt, aber ein Selektor nicht funktioniert, korrigieren Sie den Parser. Wenn Anfragen den Großteil der Zeit damit verbringen, auf Verbindungen zu warten, überprüfen Sie den Client und den Scheduler. Wenn in der Antwort Inhalte fehlen, die erst nach einer Interaktion mit der Seite erscheinen, überprüfen Sie die Anforderungen an die Darstellung.

Requests: eine praktische Wahl für synchrone Scraper

„Requests“ ist ein guter Ausgangspunkt, wenn Ihr Skript eine überschaubare Anzahl aufeinanderfolgender Abrufe durchführt und das Ziel direkt nützliches HTML zurückgibt. Der einfache Abfrageablauf erleichtert es, eine fehlgeschlagene Antwort zu untersuchen und ein Netzwerkproblem von einem Parsing-Problem zu unterscheiden.

Installiere den Client und den Parser in der virtuellen Umgebung deines Projekts:

python -m pip install requests beautifulsoup4

Verwende eine Sitzung für wiederholte Anfragen. In der Dokumentation zu „Requests“-Sitzungen wird erläutert, dass Sitzungen Cookies beibehalten und Connection-Pooling nutzen, wodurch Verbindungen zwischen Anfragen an denselben Host wiederverwendet werden können.

Dieses Beispiel gibt für jede Seite ein strukturiertes Ergebnis aus, anstatt jede abgeschlossene Anfrage als erfolgreiches Scraping zu behandeln:

import requests
from bs4 import BeautifulSoup

URLS = (
    "https://books.toscrape.com/catalogue/page-1.html",
    "https://books.toscrape.com/catalogue/page-2.html",
)


def page_title(html):
    node = BeautifulSoup(html, "html.parser").find("title")
    if node is None or not node.get_text(strip=True):
        raise ValueError("Response has no usable page title")
    return node.get_text(strip=True)


def main():
    with requests.Session() as session:
        for url in URLS:
            try:
                with session.get(url, timeout=(5, 20)) as response:
                    response.raise_for_status()
                    title = page_title(response.content)
                print({"url": url, "title": title})
            except (requests.RequestException, ValueError) as exc:
                print({"url": url, "error": str(exc)})


if __name__ == "__main__":
    main()

Das Tupel legt separate Timeouts für die Verbindung und das Lesen fest. Es garantiert nicht, dass der gesamte Vorgang innerhalb von 25 Sekunden abgeschlossen ist. Requests dokumentiert in seinen Timeout-Hinweisen sowohl das Fehlen eines Standard-Timeouts als auch den Unterschied zwischen seiner Timeout-Einstellung und einer Frist für den vollständigen Download.

Die Übergabe von Antwort-Bytes an den Parser sorgt zudem dafür, dass das Dekodieren ein bewusster Teil der Extraktion bleibt. Ein zurückgegebenes Status-Code 200 ist für dieses Beispiel erforderlich, aber ein echter Collector sollte mehr als nur das Vorhandensein eines Titels validieren. Auch ein Anmeldebildschirm, eine generische Fehlerseite oder eine Challenge-Seite kann einen solchen Status-Code aufweisen.

„Requests“ eignet sich gut, wenn die Wartbarkeit die wichtigste Einschränkung ist: ein periodischer Bericht, eine Integration mit wenigen öffentlichen Endpunkten oder eine etablierte Codebasis mit guten Tests. Man muss es nicht ersetzen, nur weil es eine asynchrone Alternative gibt. Prüfen Sie zunächst, ob Netzwerkwartezeiten tatsächlich verhindern, dass der Auftrag rechtzeitig abgeschlossen wird.

Gib der Abruf-Funktion einen stabilen Rückgabekontrakt, bevor du weitere Funktionen hinzufügst. Zeichne beispielsweise die angeforderte URL, die endgültige URL, den Status sowie entweder geparste Felder oder eine Fehlerkategorie auf. Dieser Kontrakt macht teilweise ausgeführte Läufe wiederherstellbar und verhindert, dass nachgelagerter Code von dem Antwortobjekt eines bestimmten Clients abhängig ist.

Die Einschränkung liegt in dem blockierenden Aufruf selbst. Während eine Anfrage wartet, kann dieser Thread keine weitere Anfrage verarbeiten. Ein begrenzter Thread-Pool kann zwar Parallelität ermöglichen, doch dann werden Thread-Zugehörigkeit, gemeinsamer Sitzungsstatus und Warteschlangenbegrenzungen Teil Ihres Designs. Für eine neue asyncio-Anwendung ist ein nativer asyncher Client in der Regel die passendere Wahl.

Halten Sie das Abrufen und die Extraktion getrennt, wie es im Beispiel gezeigt wird. So können Sie Selektoren anhand von gespeichertem HTML testen, ohne Netzwerkanfragen zu stellen, oder später einen anderen Client einsetzen, ohne den Parser neu schreiben zu müssen. Unser Leitfaden zum Erstellen eines Beautiful-Soup-Scrapers behandelt den Aspekt der Extraktion ausführlicher.

HTTPX: eine Schnittstelle für synchrones und asynchrones Scraping

HTTPX ist eine sinnvolle Wahl, wenn Sie einen synchronen Einstieg und einen asynchronen Pfad innerhalb derselben Bibliothek wünschen. Seine Antwortobjekte bieten vertraute Konzepte wie Statuscodes, Text, JSON und Header, doch die Umstellung erfordert dennoch eine Überprüfung des Verhaltens und nicht nur die einfache Änderung eines Imports.

Verwenden Sie das optionale HTTP/2-Extra, wenn dieses Protokoll Teil Ihrer Anforderungen ist:

python -m pip install "httpx[http2]" beautifulsoup4

Die Anführungszeichen verhindern, dass Shells die Klammern interpretieren. Ein synchroner Client kann dann Verbindungen bei wiederholten Aufrufen wiederverwenden:

import httpx

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=httpx.Timeout(20.0, connect=5.0),
) as client:
    response = client.get("https://books.toscrape.com/")
    response.raise_for_status()
    print(response.status_code, response.http_version)

Die Aktivierung von HTTP/2 ermöglicht die Aushandlung; sie zwingt jedoch nicht jeden Server dazu, es zu verwenden. Überprüfen Sie die ausgehandelte Version in der Antwort, anstatt sie aus Ihrer Konfiguration abzuleiten. Der HTTPX-HTTP/2-Leitfaden beschreibt die zusätzliche Abhängigkeit, die Konfiguration und das Fallback-Verhalten.

Verwenden Sie für sich überschneidende Anfragen einen wiederverwendbaren asynchronen Client. Das folgende Beispiel fügt ein Semaphor hinzu, sodass jeweils nur zwei Abrufvorgänge gleichzeitig in den Netzwerkabschnitt gelangen:

import asyncio

import httpx
from bs4 import BeautifulSoup

URLS = tuple(
    f"https://books.toscrape.com/catalogue/page-{page}.html"
    for page in range(1, 4)
)


async def fetch_title(client, gate, url):
    async with gate:
        try:
            response = await client.get(url)
            response.raise_for_status()
            node = BeautifulSoup(response.content, "html.parser").find("title")
            if node is None or not node.get_text(strip=True):
                raise ValueError("Response has no usable page title")
            return {"url": url, "title": node.get_text(strip=True)}
        except (httpx.HTTPError, ValueError) as exc:
            return {"url": url, "error": str(exc)}


async def main():
    gate = asyncio.Semaphore(2)
    limits = httpx.Limits(max_connections=2, max_keepalive_connections=2)
    timeout = httpx.Timeout(20.0, connect=5.0, pool=5.0)
    async with httpx.AsyncClient(
        http2=True,
        follow_redirects=True,
        limits=limits,
        timeout=timeout,
    ) as client:
        results = await asyncio.gather(
            *(fetch_title(client, gate, url) for url in URLS)
        )
    for result in results:
        print(result)


if __name__ == "__main__":
    asyncio.run(main())

Der Semaphor begrenzt Operationen auf Anwendungsebene, während der Verbindungspool die Transportressourcen begrenzt. Es handelt sich um miteinander verbundene Steuerungsmechanismen, die jedoch nicht austauschbar sind. Insbesondere bei multiplexierten HTTP/2-Streams weicht die Anzahl der laufenden Anfragen von der Anzahl der offenen Verbindungen ab.

Dieses kleine Beispiel erstellt für jede URL eine Aufgabe. Bei einem umfangreichen oder kontinuierlichen Crawl sollten Sie stattdessen eine begrenzte Warteschlange mit einer festen Anzahl von Workern verwenden. Andernfalls kann es passieren, dass Sie eine riesige Sammlung wartender Aufgaben zuweisen, obwohl nur wenige das Netzwerk erreichen. Die HTTPX-Async-Dokumentation empfiehlt zudem, Clients wiederzuverwenden, anstatt sie innerhalb einer Busy-Loop wiederholt neu zu erstellen.

Es gibt zwei Details bei der Migration, die es wert sind, sofort getestet zu werden. Erstens folgt HTTPX standardmäßig keinen Weiterleitungen, weshalb diese in den Beispielen explizit aktiviert werden. Zweitens stellen die Timeouts für „connect“, „read“, „write“ und den Pool unterschiedliche Fehlerzustände dar. Ein Pool-Timeout kann darauf hindeuten, dass lokale Aufgaben auf eine Verbindung warten, und nicht, dass der Zielserver nicht erreichbar ist. Lesen Sie den Kompatibilitätsleitfaden und die Timeout-Definitionen, bevor Sie Annahmen aus einem anderen Client übernehmen.

Halten Sie neben dem Scraper einen kleinen Satz an Migrations-Fixtures bereit: eine normale Antwort, eine Weiterleitung, einen Fehlerzustand und eine Antwort, bei der ein erforderliches Feld fehlt. Vergleichen Sie die endgültigen Datensätze und nicht die Objektdarstellungen der Clients. Wenn ein Test abweicht, entscheiden Sie, ob das neue Verhalten beabsichtigt ist, bevor Sie die nachgelagerte Parsing-Logik ändern. Dies bietet Ihnen zudem eine praktische Rollback-Prüfung, falls sich das Ziel nach der Bereitstellung anders verhält.

HTTPX hat sich durch diese Kombination aus Schnittstellen und Steuerungsmöglichkeiten einen Platz unter den besten Python-HTTP-Clients für das Web-Scraping verdient. Entscheiden Sie sich dafür, wenn diese Funktionen Ihr Projekt vereinfachen, und überprüfen Sie das tatsächliche Verhalten des Zielservers, bevor Sie neue Protokolle flächendeckend aktivieren.

aiohttp: Kontrolle für einen asyncio-basierten Crawler

aiohttp eignet sich besonders gut, wenn die umgebende Anwendung bereits asynchron ist und eine explizite Kontrolle über die Verbindungen zu jedem Ziel erfordert. Seine Client-Sitzung verwaltet wiederverwendbare Verbindungen und Cookies, während sein Connector es Ihnen ermöglicht, Gesamt- und Host-spezifische Limits festzulegen.

Installieren Sie den Client und den Parser:

python -m pip install aiohttp beautifulsoup4

Hier ist dieselbe Aufgabe zur Titel-Extraktion mit einer aiohttp-Sitzung. Der Connector erlaubt insgesamt höchstens zwei Verbindungen und höchstens zwei zur Übungswebsite. Ein Semaphor begrenzt separat die aktiven Abrufvorgänge:

import asyncio

import aiohttp
from bs4 import BeautifulSoup

URLS = tuple(
    f"https://books.toscrape.com/catalogue/page-{page}.html"
    for page in range(1, 4)
)


async def fetch_title(session, gate, url):
    async with gate:
        try:
            async with session.get(url) as response:
                response.raise_for_status()
                html = await response.read()
            node = BeautifulSoup(html, "html.parser").find("title")
            if node is None or not node.get_text(strip=True):
                raise ValueError("Response has no usable page title")
            return {"url": url, "title": node.get_text(strip=True)}
        except (aiohttp.ClientError, asyncio.TimeoutError, ValueError) as exc:
            return {"url": url, "error": str(exc)}


async def main():
    gate = asyncio.Semaphore(2)
    connector = aiohttp.TCPConnector(limit=2, limit_per_host=2)
    timeout = aiohttp.ClientTimeout(total=30, sock_connect=5, sock_read=20)
    async with aiohttp.ClientSession(
        connector=connector,
        timeout=timeout,
    ) as session:
        results = await asyncio.gather(
            *(fetch_title(session, gate, url) for url in URLS)
        )
    for result in results:
        print(result)


if __name__ == "__main__":
    asyncio.run(main())

Der Antwortkontext wird geschlossen, nachdem sein Body verarbeitet wurde, und die Sitzung wird geschlossen, sobald die Erfassung abgeschlossen ist. Diese Lebenszyklusgrenzen sind Teil des Beispiels und keine optionale Bereinigung. Das Offenlassen von Antwortströmen kann verhindern, dass Verbindungen in den Pool zurückkehren.

Die Dokumentation zum aiohttp-Advanced-Client erläutert die Beschränkungen des Connectors und das Sitzungsverhalten. Beschränkungen pro Host sind besonders nützlich, wenn ein Crawl mehrere Ursprungsserver besucht: Ein schneller oder stark ausgelasteter Ursprungsserver sollte nicht die Möglichkeit haben, alle Ressourcen zu verbrauchen, ohne dass eine bewusste Planungsentscheidung getroffen wurde.

Das Timeout-Objekt unterscheidet zudem zwischen einem Gesamt-Timeout für die Anfrage und den Einstellungen für Verbindung und Socket-Lesevorgänge. Die Client-Schnellstartanleitung dokumentiert diese Felder. Das Gesamt-Timeout gilt für den Anfragevorgang; die Wartezeit außerhalb dieser Anfrage, beispielsweise am Semaphor Ihrer Anwendung, fällt weiterhin unter das übergeordnete Job-Budget.

Entscheiden Sie sich für aiohttp wegen seiner asyncio-Integration und -Steuerungsmöglichkeiten, anstatt davon auszugehen, dass „async“ unbegrenzten Durchsatz bedeutet. Eine Website kann eine niedrige Anfragerate vorschreiben, oder Ihr HTML-Parsing könnte zum Engpass werden. Keine dieser Einschränkungen verschwindet, wenn sich mehr Anfragen überschneiden.

Bei einem Crawler im Produktivbetrieb sollten Sie vermeiden, dass das Parsen und die Speicherung die Ereignisschleife monopolisieren. Das Parsen der kleinen Seiten in diesem Beispiel ist als Lehrbeispiel akzeptabel. Aufwändiges Parsen oder synchrone Datenbankoperationen erfordern eine separate Ausführungsstrategie, damit die Netzwerkaufgaben weiter vorankommen können.

aiohttp bietet auch Streaming- und WebSocket-Unterstützung, doch diese Funktionen sind nur dann von Bedeutung, wenn Ihre Ziele oder Ihre Infrastruktur sie nutzen. Wenn HTTP/2 eine Anforderung ist, prüfen Sie einen Client, der diese Fähigkeit dokumentiert. Wenn Ihr Skript vollständig synchron ist, sollten Sie vor der Migration die Betriebskosten für die Einführung einer Ereignisschleife kalkulieren.

Spezialisierte Clients: urllib3, curl_cffi und Niquests

Diese Clients sollten in Betracht gezogen werden, wenn die Standardauswahl nicht die Kontrolle oder das Protokollverhalten bietet, das Ihre Arbeitslast erfordert. Jeder hat einen anderen Daseinsgrund. Bewerten Sie diesen Grund direkt, anstatt zusätzliche Funktionen als automatisches Upgrade zu betrachten.

urllib3: direkte Kontrolle über Pools und Wiederholungsversuche

urllib3 ist nützlich, wenn Sie eine Abstraktion für das Abrufen von Daten erstellen und enger mit Verbindungspools arbeiten möchten. Requests nutzt es im Hintergrund, aber die direkte Verwendung bietet Ihrem Code eine andere Schnittstelle und mehr Verantwortung für die Verarbeitung von Antworten.

Das urllib3-Benutzerhandbuch dokumentiert den PoolManager, Antwortkörper und die Anforderungskonfiguration. Dieses kompakte Beispiel überprüft den Status explizit:

import urllib3

http = urllib3.PoolManager(
    timeout=urllib3.Timeout(connect=5.0, read=20.0),
)
try:
    response = http.request("GET", "https://books.toscrape.com/", retries=False)
    if response.status != 200:
        raise RuntimeError(f"Unexpected HTTP status: {response.status}")
    print(len(response.data))
finally:
    http.clear()

Der direkte Zugriff auf den Pool ist sinnvoll, wenn du das Transportverhalten in einem größeren internen System standardisieren musst. Für einen gewöhnlichen Scraper ermöglichen es dir die übergeordneten Clients jedoch möglicherweise, denselben Auftrag mit weniger unterstützendem Code umzusetzen. Wähle urllib3 nicht allein deshalb aus, weil eine Bibliothek, die du bereits verwendest, davon abhängt.

curl_cffi: Nachahmung des Browser-Transports

curl_cffi stellt eine „Requests“-ähnliche Schnittstelle bereit, die auf einem curl-basierten Transport aufbaut. Sein dokumentiertes Alleinstellungsmerkmal ist die Nachahmung unterstützter Browser-TLS- und HTTP/2-Fingerabdrücke. Diese betreffen Details der Verbindung und des Protokollaustauschs, die über einen User-Agent-Header hinausgehen.

Die Projektdokumentation beschreibt synchrone und asynchrone Schnittstellen, und die Schnellstartanleitung zeigt die Profilauswahl. Dies ist eine spezielle Wahl, wenn du festgestellt hast, dass die Transportkompatibilität für deinen zulässigen Datenerfassungs-Workflow von Bedeutung ist.

Ein Imitationsprofil verwandelt den Client nicht in einen Browser. Es führt weder das JavaScript einer Seite aus, noch klickt es auf einen Einwilligungsdialog, noch garantiert es, dass ein Server die Anfrage akzeptiert. Eine abgelehnte Antwort kann andere Ursachen haben, darunter den Authentifizierungsstatus oder ein erschöpftes Anfragekontingent.

Überprüfen Sie die Verfügbarkeit des nativen „Wheel“ für Ihre Bereitstellungsplattform und testen Sie das ausgewählte Profil mit Ihrem Zielsatz. Vermeiden Sie es, sich auf ein nicht näher spezifiziertes, sich änderndes Profil zu verlassen, wenn Reproduzierbarkeit wichtig ist. Dokumentieren Sie die Paket- und Profilauswahl zusammen mit Ihrer Bereitstellungskonfiguration, damit eine Verhaltensänderung untersucht werden kann.

Niquests: umfassendere Protokollunterstützung

Niquests ist eine weitere Option für Entwickler, die eine vertraute, hochrangige Anfrage-Schnittstelle mit neueren HTTP-Funktionen wünschen. Die offizielle Dokumentation beschreibt den synchronen und asynchronen Betrieb sowie die Unterstützung von HTTP/1.1, HTTP/2 und HTTP/3.

Die Protokollunterstützung ist eine Fähigkeit, kein Versprechen für jede einzelne Verbindung. Serverunterstützung, Netzwerkbedingungen, Proxys und Aushandlung beeinflussen, was tatsächlich verwendet wird. Überprüfen Sie das Verhalten in der Umgebung, in der Ihr Scraper tatsächlich ausgeführt wird, anstatt sich auf eine erfolgreiche Anfrage von Ihrem Laptop zu verlassen.

Betrachten Sie eine Migration als Abhängigkeits- und Verhaltensänderung, selbst wenn ein Projekt sich selbst als Ersatz für eine andere Bibliothek bezeichnet. Nutzen Sie eine isolierte Umgebung, überprüfen Sie die aufgelösten Abhängigkeiten und testen Sie Cookies, Weiterleitungen, Proxys, Ausnahmen und gestreamte Antworten, bevor Sie einen bestehenden Job umstellen.

Für alle drei Spezialistenoptionen lautet die praktische Frage gleich: Welche festgestellte Einschränkung löst diese Lösung? Wenn Sie keine nennen können, behalten Sie den einfacheren Client bei und investieren Sie zunächst in die Validierung der Extraktion, die Überwachung oder die Zeitplanung.

Zuverlässigkeitseinstellungen, die wichtiger sind als der Name des Clients

Der Client legt die verfügbaren Steuerungsmöglichkeiten fest; Ihre Anwendung entscheidet, wie diese genutzt werden. Ein Scraper mit sinnvoller Wiederverwendung von Verbindungen und Fehlerbehandlung ist einfacher zu betreiben als einer, der wiederholt die Bibliothek wechselt, während er dieselbe unbegrenzte Anforderungsschleife beibehält.

Legen Sie eine umfassende Zeitlimit-Strategie fest

Unterscheiden Sie zwischen dem Aufbau einer Verbindung, dem Warten auf Antwortbytes, dem Warten auf einen Pool-Slot und der Gesamtzeit, die Ihr Job für eine URL aufwenden darf. Das sind unterschiedliche Zeitbudgets. Wiederholungsversuche und Warteschlangenverzögerungen können die tatsächliche Dauer deutlich verlängern, als es ein einzelnes Timeout-Argument vermuten lässt.

Bei einem asyncio-Workflow kann ein übergeordneter Task-Timeout eine umfassendere Frist durchsetzen. Entscheiden Sie, ob diese Frist Warteschlangen und Backoff einschließt, und protokollieren Sie, in welcher Phase die Frist abgelaufen ist. Behalten Sie auch die Timeouts auf Client-Ebene bei, da diese Aufschluss über den konkreten Netzwerkvorgang geben, der ins Stocken geraten ist.

Wiederholungsversuche bei behebbaren Fehlern mit begrenztem Budget

Eine Wiederholungsrichtlinie sollte zulässige Fehler, zulässige Methoden, eine maximale Anzahl von Versuchen und eine Verzögerungsstrategie festlegen. Bei einem schreibgeschützten GET-Collector können ausgewählte Verbindungsfehler und vorübergehende Antwortstatus einen erneuten Versuch rechtfertigen. Wiederholte Authentifizierungsfehler oder unveränderte Parsing-Fehler erfordern hingegen in der Regel eine Untersuchung.

Anfragen können die Wiederholungsrichtlinie von urllib3 über einen `HTTPAdapter` nutzen. Dieses Beispiel ersetzt die einfache Sitzungs-Einrichtung, wenn Ihre Arbeitslast dieses Verhalten erfordert:

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

retry = Retry(
    total=3,
    backoff_factor=0.5,
    status_forcelist=(429, 500, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

with requests.Session() as session:
    session.mount("https://", HTTPAdapter(max_retries=retry))
    with session.get("https://books.toscrape.com/", timeout=(5, 20)) as response:
        response.raise_for_status()
        print(response.status_code)

Hier ist „drei“ das Wiederholungsbudget nach dem ersten Versuch, nicht die Gesamtzahl der Versuche. Die urllib3-Referenz zum Wiederholungsversuch erläutert den Methodenfilter, die Statusliste, das Backoff und die Handhabung von „Retry-After“. Eine vom Server vorgegebene Wartezeit kann die Jobdauer verlängern; berücksichtigen Sie dies daher in Ihrer allgemeinen Fristrichtlinie.

Gehen Sie nicht davon aus, dass die Wiederholungsoption eines anderen Clients dasselbe bedeutet. Die Transport-Wiederholungsversuche von HTTPX beheben Verbindungsfehler und Verbindungszeitüberschreitungen; sie implementieren keine allgemeine Wiederholungsrichtlinie für 429- oder 503-Antworten. Halten Sie Anwendungswiederholungen an einer Stelle zusammen, damit verschachtelte Ebenen die Versuche nicht unbemerkt vervielfachen.

Trennen Sie Parallelitäts-, Raten- und Speichergrenzen

Ein Parallelitätslimit regelt die gleichzeitige Ausführung von Aufgaben. Ein Ratenlimit regelt, wie häufig im Laufe der Zeit neue Aufgaben gestartet werden. Zwei schnell ausgeführte, parallel laufende Anfragen können dennoch eine hohe Anfragerate erzeugen; daher stellen die Limits in den Beispielen keine vollständige Ratenregelung dar.

Verwenden Sie eine originbasierte Planung und reagieren Sie auf Server-Feedback. Begrenzen Sie sowohl die Warteschlange als auch die aktiven Aufgaben und speichern Sie abgeschlossene Datensätze schrittweise, anstatt jede Antwort bis zum Ende aufzubewahren. Große Downloads erfordern einen Streaming-Pfad und ein explizites Byte-Limit; unser Python-Leitfaden zum Dateidownload behandelt diesen damit verbundenen Arbeitsablauf.

Überprüfen Sie Antworten und schützen Sie den Sitzungsstatus

Verfolgen Sie endgültige URLs, Statuscodes, Inhaltstypen und erforderliche extrahierte Felder. Klassifizieren Sie Fehler separat, damit eine Änderung der Selektoren nicht wie ein Transportausfall erscheint. Bewahren Sie genügend bereinigten Kontext auf, um Probleme zu diagnostizieren, ohne Anmeldedaten oder sensible Antwortinhalte zu protokollieren.

Verwenden Sie separate Sitzungen für verschiedene authentifizierte Identitäten. Lassen Sie die TLS-Zertifikatsüberprüfung aktiviert und laden Sie Anmeldedaten aus der Bereitstellungsumgebung. Wenn Anfragen Proxys verwenden, testen Sie deren Konfiguration und Verbindungsverhalten explizit; dieser Leitfaden zu Proxys mit Python Requests bietet einen gezielten Einstieg.

So vergleichen Sie Clients anhand Ihrer eigenen Scraping-Workload

Messen Sie die Anzahl der gültig extrahierten Datensätze pro Zeiteinheit sowie die Fehlerquote und die Ressourcennutzung. Ein Client, der schnell eine Fehlerseite herunterlädt, hat die Geschäftsaufgabe nicht abgeschlossen. Die besten Python-HTTP-Clients für das Web-Scraping sollten anhand Ihrer tatsächlichen Ergebnisanforderungen bewertet werden.

Erstellen Sie einen kleinen repräsentativen Datensatz, bevor Sie Vergleiche anstellen. Beziehen Sie dabei die Arten von Antworten ein, auf die Ihr Scraper stößt: eine normale Seite, eine Weiterleitung, eine langsame Antwort, einen leeren Body, eine Fehlerantwort und eine Seite, auf der ein erwartetes Feld fehlt. Durch kontrollierte lokale Testdaten lassen sich diese Fälle wiederholbar simulieren, ohne die Website eines anderen einem Lasttest auszusetzen.

Achten Sie auf Vergleichbarkeit

Messen

Grund

URLs und Extraktionsregeln

Gültige Datensätze und Validierungsfehler

Bestätigt gleichwertige Arbeit

Richtlinie zur Wiederverwendung und zum Aufwärmen von Sitzungen

Verbindungs-Overhead und Latenz im stationären Zustand

Vermeidung des Vergleichs von „warmen“ Pools mit „kalten“ Verbindungen

Parallelität und Drosselung pro Ursprung

Durchsatz und Antworten zur Ratenbegrenzung

Verhindert, dass ein Test durch das Senden von mehr Datenverkehr den Vorzug erhält

Timeout- und Wiederholungsbudgets

Versuche, Fehler und Abschlusszeit

Deckt verborgene Arbeitsschritte hinter einem erfolgreichen Ergebnis auf

Verarbeitung und Speicherung von Antworten

Speicher, CPU und dauerhaft gespeicherte Datensätze

Bezieht den Rest der Pipeline mit ein

Erfassen Sie bei jedem Durchlauf die Python- und Abhängigkeitsversionen. Trennen Sie das Verhalten bei der ersten Anfrage vom Verhalten im stationären Zustand, wiederholen Sie das Experiment und untersuchen Sie die Latenz im Slow-Tail, anstatt nur einen Durchschnittswert anzugeben. Achten Sie auf Konsistenz bei der Analyse und Speicherung, damit diese Kosten einen Vergleich der Clients nicht verzerren.

Nutzen Sie das Ergebnis, um eine Änderung zu begründen, nicht um einen dauerhaften Sieger zu erklären. Wenn alle Kandidaten die Frist des Auftrags problemlos einhalten, können Lesbarkeit und Vertrautheit im Betrieb den Ausschlag geben. Wenn eine Variante den Durchsatz verbessert, aber schwerwiegendere Fehlermodi mit sich bringt, dokumentieren Sie diesen Kompromiss vor der Migration. Führen Sie die alte und die neue Implementierung mit denselben gespeicherten Testumgebungen aus und vergleichen Sie die extrahierten Datensätze, bevor Sie den Produktionsdatenverkehr umstellen.

Wenn Sie mehr als einen HTTP-Client benötigen

Ein Wechsel des Clients ist nur dann sinnvoll, wenn der Client die begrenzende Ebene darstellt. Wenn die HTML-Antwort die Daten nicht enthält, prüfen Sie, wie die Seite diese bezieht. Die Website stellt möglicherweise einen zulässigen JSON-Endpunkt bereit, gibt eingebettete strukturierte Daten zurück oder füllt die Benutzeroberfläche nach dem Laden über JavaScript auf.

Ein HTTP-Client kann einen direkt zugänglichen Endpunkt abrufen, ohne die Benutzeroberfläche zu rendern. Das sorgt oft für eine einfachere Pipeline. Ein Workflow, der jedoch das Anklicken von Steuerelementen, das Scrollen, das Warten auf einen Seitenzustand oder das Ausführen einer Abfolge von Interaktionen erfordert, benötigt möglicherweise die Ausführung im Browser.

Die Nachahmung des Browser-Transports und die Browserausführung sind unterschiedliche Funktionen. Ebenso bietet das HTTP/2-Multiplexing keine JavaScript-Laufzeitumgebung. Stellen Sie fest, welche Funktion fehlt, bevor Sie ein anderes Tool einführen, und halten Sie den Ausgabekontrakt konsistent, damit Ihr Parser und Ihre Speicherlogik stabil bleiben.

Ein verwalteter Browserdienst ist eine Option, wenn die Seitenausführung und Interaktion die Aufgaben sind, die Sie delegieren müssen. Ein verwalteter Scraping-Endpunkt ist eine weitere Option, wenn der Aufwand in der Handhabung wiederholter Zugriffe, im Proxy-Routing, in Wiederholungsversuchen oder in der Rendering-Konfiguration liegt. Beides sollte nicht als Grund dafür angesehen werden, die Antwortvalidierung aus Ihrer Anwendung zu entfernen.

Bei einer gemischten Zielgruppe sollten Sie eine Eskalationsrichtlinie nach Ziel oder festgestelltem Fehler beibehalten, anstatt alles über den aufwendigsten Pfad zu leiten. Beginnen Sie mit der Antwort, die Sie direkt abrufen können, überprüfen Sie die benötigten Felder und leiten Sie nur die Workflows, die zusätzliche Funktionen erfordern, an einen anderen Fetcher weiter. Dokumentieren Sie, warum dieser Pfad gewählt wurde, und messen Sie dessen Erfolg anhand derselben Extraktionsprüfungen.

Ihr Python-Client kann bei beiden Ansätzen als Schnittstelle dienen. Die wichtige Entscheidung ist, welche Komponente für das Abrufen, die Darstellung und das Wiederholungsverhalten zuständig ist, damit nicht zwei Ebenen unabhängig voneinander denselben fehlgeschlagenen Vorgang wiederholen.

Wichtige Erkenntnisse

  • Behalten Sie „Requests“ bei, wenn ein synchroner Scraper seine Frist einhält und sein Verhalten gut verstanden ist. Verwenden Sie eine Sitzung und explizite Timeouts.
  • Wählen Sie HTTPX für Flexibilität bei synchronen und asynchronen Vorgängen sowie optionales HTTP/2 oder aiohttp für eine asyncio-Pipeline mit direkter Verbindungskontrolle pro Host.
  • Setzen Sie einen speziellen Transport nur bei einer verifizierten Anforderung ein und testen Sie dessen Abhängigkeiten sowie das Protokollverhalten in Ihrer Produktionsumgebung.
  • Steuern Sie gleichzeitige Anfragen, die Anfragerate, die Warteschlangengröße, Wiederholungsversuche und die Antwortgröße separat.
  • Vergleichen Sie gültige extrahierte Datensätze und die End-to-End-Zuverlässigkeit, nicht nur die Zeit, die zum Empfang einer HTTP-Antwort benötigt wird.

FAQ

Kann ich einen synchronen HTTP-Client aus einer asynchronen Funktion aufrufen?

Ja, aber ein direkter blockierender Aufruf verhindert, dass der Event-Loop-Thread weiterläuft, bis er zurückkehrt. Verwenden Sie einen nativen asynchronen Client oder verlagern Sie blockierende E/A-Vorgänge in einen begrenzten, threadbasierten Pfad. Python dokumentiert zu diesem Zweck `asyncio.to_thread`. Legen Sie außerdem Netzwerk-Timeouts fest: Das Abbrechen des `await`-Befehls stoppt nicht unbedingt einen bereits laufenden, blockierenden Vorgang im Worker-Thread.

Wie sollte ich die Textkodierung vor dem Parsen von HTML handhaben?

Behandeln Sie die Dekodierung als eine vom Herunterladen getrennte Entscheidung. Bewahren Sie die Antwortbytes auf, falls Sie Kodierungsprobleme untersuchen müssen, und überprüfen Sie den deklarierten Zeichensatz anhand der Metadaten des Dokuments. Wenn der dekodierte Text Ersatzzeichen oder verstümmelte Akzente enthält, korrigieren Sie die Kodierungsannahme, bevor Sie Selektoren ausführen oder Felder speichern. Eine erfolgreiche Anfrage bedeutet nicht zwangsläufig, dass der Text korrekt dekodiert wurde.

Sollte ich die Versionen der HTTP-Clients in einem Scraper festlegen?

Ja, verwenden Sie reproduzierbare Abhängigkeitsversionen für bereitgestellte Jobs und aktualisieren Sie diese gezielt. Erfassen Sie die Python-Version und den aufgelösten Abhängigkeitssatz und testen Sie Aktualisierungen anhand repräsentativer Antworten, Weiterleitungen, Fehler und Extraktions-Fixtures. Reproduzierbarkeit erleichtert die Diagnose von Regressionen; sie sollte jedoch nicht bedeuten, dass Abhängigkeiten auf unbestimmte Zeit eingefroren werden. Planen Sie Aktualisierungen ein und überprüfen Sie im Rahmen der routinemäßigen Wartung die Release-Notes der ausgewählten Projekte.

Fazit

Wählen Sie den kleinsten HTTP-Client, der für die von Ihnen auszuführende Aufgabe geeignet ist. „Requests“ ist eine praktische synchrone Basis. „HTTPX“ bietet einen Mittelweg zwischen synchronem und asynchronem Code mit optionalem HTTP/2. „aiohttp“ eignet sich für einen auf „asyncio“ basierenden Collector, der eine explizite Verbindungsverwaltung benötigt. „urllib3“, „curl_cffi“ und „Niquests“ sind eine Bewertung wert, wenn ihre spezifischen Fähigkeiten eine Anforderung erfüllen, die Sie nachweisen können.

Legen Sie vor der Migration fest, was Erfolg bedeutet: Die erforderlichen Felder wurden extrahiert, der Datensatz wurde gespeichert, der Auftrag wurde fristgerecht erledigt und fehlgeschlagene URLs können untersucht oder sicher erneut aufgerufen werden. Vergleichen Sie dann die Kandidaten unter gleichen Rahmenbedingungen. Wiederverwendete Sitzungen, begrenzter Arbeitsaufwand und nützliche Diagnosemöglichkeiten verdienen oft mehr Beachtung als die Bibliothek selbst.

Wenn der Betrieb der Zugriffsebene Zeit kostet, die Ihnen für die Extraktion fehlt, testen Sie die Scraper-API von WebScrapingAPI an einem repräsentativen Ziel. Sie bietet verwaltetes Proxy-Routing, Wiederholungsversuche und optionales JavaScript-Rendering, während Ihr Python-Code weiterhin für die Überprüfung der zurückgegebenen Daten zuständig ist. Behalten Sie die gleichen Validierungsregeln bei, unabhängig davon, ob Sie Daten direkt abrufen oder einen verwalteten Endpunkt nutzen, damit eine Änderung des Übertragungswegs niemals unbemerkt dazu führt, dass sich die Kriterien für einen erfolgreichen Scrape ändern.

Über den Autor

Mihnea-Octavian Manolache, Full-Stack-Entwickler @ WebScrapingAPI

Mihnea-Octavian Manolache

Full-Stack-Entwickler

Mihnea-Octavian Manolache ist Full-Stack- und DevOps-Entwickler bei WebScrapingAPI. Er entwickelt Produktfunktionen und sorgt für die Wartung der Infrastruktur, die den reibungslosen Betrieb der Plattform gewährleistet.

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026
Anleitungen

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026

TL;DR: Kurz gesagt: Web-Scraping mit AWS Lambda funktioniert am besten, wenn jeder Aufruf kurz, begrenzt und unabhängig wiederholbar ist. Beginnen Sie mit direktem HTTP, AWS SAM und S3 und fügen Sie erst dann SQS, Container, Browser-Rendering, Proxys oder eine verwaltete Abrufebene hinzu, wenn sich im Rahmen der Arbeitslast herausstellt, dass diese erforderlich sind.

Suciu Dan26 min read
Artikel lesen
So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren
Anleitungen

So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren

TL;DR: GoSpider ist ein Befehlszeilen-Crawler zum Auffinden von URLs, kein vollständiger Scraper für strukturierte Daten. Diese Anleitung zur Verwendung von GoSpider zeigt einen begrenzten Crawl, die saubere Verarbeitung der Ausgabe, die Übergabe von Colly an CSV sowie einen Diagnosepfad für 403-Antworten oder Seiten, die eine JavaScript-Darstellung erfordern.

Suciu Dan20 min read
Artikel lesen
Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten
Anleitungen

Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten

TL;DR: Redfin stellt versteckte API-Endpunkte zur Verfügung, die strukturiertes JSON für Immobilienangebote zurückgeben, wodurch das fragile HTML-Parsing vollständig übersprungen werden kann. Diese Anleitung führt Sie durch den Aufbau eines Python-Scrapers, der Miet- und Verkaufsdaten extrahiert, nach Standort sucht, neue Angebote über XML-Sitemaps überwacht und saubere Ergebnisse in CSV oder JSON exportiert.

Suciu Dan11 min read
Artikel lesen

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.