Asynchrones Web Scraping mit AIOHTTP in Python

Entdecken Sie AIOHTTP für Web Scraping! Lernen Sie Einrichtung, Funktionen und fortgeschrittene Techniken sowie einen Vergleich mit Requests zur effizienten Datenextraktion kennen.
15 min read
web scraping with aiohttp and python blog image

In diesem Leitfaden erkunden wir Folgendes:

  • Was AIOHTTP ist und welche wichtigen Funktionen es bietet
  • Einen Schritt-für-Schritt-Abschnitt zur Verwendung von AIOHTTP für Web Scraping
  • Fortgeschrittene Techniken für Web Scraping mit AIOHTTP
  • Ein Vergleich zwischen AIOHTTP und Requests für die Handhabung automatisierter Requests

Dann legen wir mal los!

Was ist AIOHTTP?

AIOHTTP ist ein asynchrones Client-/Server-HTTP-Framework, das auf Pythons asyncio aufbaut. Im Gegensatz zu herkömmlichen HTTP-Clients verwendet AIOHTTP Client-Sitzungen, um Verbindungen über mehrere Requests hinweg aufrechtzuerhalten. Das macht es zu einer effizienten Wahl für sitzungsbasierte Aufgaben mit hoher Parallelität.

⚙️ Merkmale

  • Unterstützt sowohl die Client- als auch die Serverseite des HTTP-Protokolls.
  • Bietet nativen Support für WebSockets (sowohl Client als auch Server).
  • Bietet Middleware und steckbares Routing für Webserver.
  • Verarbeitet das Streaming großer Daten effizient.
  • Umfasst die Persistenz von Client-Sitzungen, ermöglicht die Wiederverwendung von Verbindungen und reduziert den Overhead bei mehreren Requests.

Scraping mit AIOHTTP: Schritt-für-Schritt-Anleitung

Im Zusammenhang mit Web Scraping ist AIOHTTP lediglich ein HTTP-Client, der den rohen HTML-Inhalt einer Seite abruft. Zum Parsen und Extrahieren von Daten aus diesem HTML benötigen Sie dann einen HTML-Parser wie BeautifulSoup.

In diesem Abschnitt erfahren Sie, wie Sie AIOHTTP für Web Scraping mit BeautifulSoup verwenden!

Warnung: Obwohl AIOHTTP hauptsächlich in der Anfangsphase des Prozesses verwendet wird, werden wir Sie durch den gesamten Scraping-Workflow führen. Wenn Sie an fortschrittlicheren AIOHTTP-Web-Scraping-Techniken interessiert sind, können Sie zum nächsten Kapitel nach Schritt 3 übergehen.

Schritt Nr. 1: Einrichtung Ihres Scraping-Projekts

Stellen Sie sicher, dass Python 3+ auf Ihrem Computer installiert ist. Falls nicht, laden Sie es von der offiziellen Website herunter und folgen Sie den Installationsanweisungen.

Erstellen Sie dann mit diesem Befehl ein Verzeichnis für Ihr AIOHTTP-Scraping-Projekt:

mkdir aiohttp-scraper

Navigieren Sie in dieses Verzeichnis und richten Sie eine virtuelle Umgebung ein:

cd aiohttp-scraper
python -m venv env

Öffnen Sie den Projektordner in Ihrer bevorzugten Python-IDE. Visual Studio Code mit der Python-Erweiterung oder PyCharm Community Edition sind beides gültige Auswahlmöglichkeiten.

Erstellen Sie nun innerhalb des Projektordners eine scraper.py-Datei. Zunächst wird sie leer sein, aber Sie werden bald die Scraping-Logik hinzufügen.

Aktivieren Sie in Ihrem Terminal der IDE die virtuelle Umgebung. Unter Linux oder macOS verwenden Sie:

./env/bin/activate

Äquivalent, unter Windows, führen Sie Folgendes aus:

env/Scripts/activate

Großartig! Sie sind fertig und können loslegen.

Schritt Nr. 2: Einrichtung der Scraping-Bibliotheken

Wenn die virtuelle Umgebung aktiviert ist, installieren Sie AIOHTTP und BeautifulSoup mit dem folgenden Befehl:

pip install aiohttp beautifulsoup4

Dadurch wird aiohttp und beautifulsoup4 zu den Abhängigkeiten Ihres Projekts hinzugefügt.

Importieren Sie sie in Ihr scraper.py-Skript:

import asyncio
import aiohttp 
from bs4 import BeautifulSoup

Beachten Sie, dass aiohttp die asyncio benötigt, um zu funktionieren.

Fügen Sie nun den folgenden async-Funktionsablauf in Ihre scrper.py-Datei ein:

async def scrape_quotes():
    # Scraping logic...

# Run the asynchronous function
asyncio.run(scrape_quotes())

scrape_quotes() definiert eine asynchrone Funktion, bei der Ihre Scraping-Logik gleichzeitig und ohne Blockierung ausgeführt wird. Schließlich startet asyncio.run(scrape_quotes()) und führt die asynchrone Funktion aus.

Fantastisch! Sie können mit dem nächsten Schritt in Ihrem Scraping-Workflow fortfahren.

Schritt Nr. 3: Holen Sie sich den HTML-Code der Zielseite

In diesem Beispiel sehen Sie, wie Sie Daten von der Website „Quotes to Scrape“ scrapen:

Die Zielwebsite

Mit Bibliotheken wie Requests oder AIOHTTP würden Sie einfach eine GET-Request stellen und direkt den HTML-Inhalt der Seite erhalten. AIOHTTP folgt jedoch einem anderen Request-Lebenszyklus.

Die AIOHTTP-Primärkomponente ist die ClientSession, die einen Pool von Verbindungen verwaltet und Keep-Alive standardmäßig unterstützt. Anstatt für jede Request eine neue Verbindung zu öffnen, werden Verbindungen wiederverwendet, was die Leistung verbessert.

Bei einer Request sind in der Regel drei Schritte erforderlich:

  1. Eröffnung einer Sitzung durch ClientSession().
  2. Asynchrones Senden der GET-Request mit session.get().
  3. Zugriff auf die Response-Daten mit Methoden wie await response.text().

Dieses Design ermöglicht der Ereignisschleife, verschiedene mit contexts zwischen den Operationen ohne Blockieren zu verwenden, was es ideal für Aufgaben mit hoher Parallelität macht.

Vor diesem Hintergrund können Sie AIOHTTP verwenden, um das HTML der Homepage mit dieser Logik abzurufen:

async with aiohttp.ClientSession() as session:
    async with session.get("http://quotes.toscrape.com") as response:
        # Access the HTML of the target page
        html = await response.text()

Hinter den Kulissen sendet AIOHTTP die Request an den Server und wartet auf die Response, die den HTML-Code der Seite enthält. Sobald die Response eingegangen ist, extrahiert await response.text() den HTML-Inhalt als Zeichenfolge.

Drucken Sie die Variable html und Sie werden Folgendes sehen:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quotes to Scrape</title>
    <link rel="stylesheet" href="/static/bootstrap.min.css">
    <link rel="stylesheet" href="/static/main.css">
</head>
<body>
    <!-- omitted for brevity... -->
</body>
</html>

Gut gemacht! Sie haben den HTML-Inhalt der Zielseite erfolgreich abgerufen. Es ist an der Zeit, diesen Inhalt zu parsen und die benötigten Daten zu extrahieren.

Schritt Nr. 4: Parsen des HTML

Übergeben Sie den HTML-Inhalt an den BeautifulSoup-Constructor, um ihn zu parsen:

# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(html, "html.parser")

html.parser ist der Standard-Python-HTML-Parser, der zur Verarbeitung des Inhalts verwendet wird.

Das Objekt soup enthält das geparste HTML und bietet Methoden zur Extraktion der benötigten Daten.

AIOHTTP hat das Abrufen des HTML übernommen und jetzt wechseln Sie mit BeautifulSoup zur typischen Daten-Parsing-Phase. Weitere Einzelheiten finden Sie in unserem Tutorial zu BeautifulSoup Web Scraping.

Schritt Nr. 5: Schreiben der Datenextraktionslogik

Mit folgendem Code können Sie die Zitate von der Seite scrapen:

# Where to store the scraped data
quotes = []

# Extract all quotes from the page
quote_elements = soup.find_all("div", class_="quote")

# Loop through quotes and extract text, author, and tags
for quote_element in quote_elements:
    text = quote_element.find("span", class_="text").get_text().get_text().replace("“", "").replace("”", "")
    author = quote_element.find("small", class_="author")
    tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

    # Store the scraped data
    quotes.append({
        "text": text,
        "author": author,
        "tags": tags
    })

Dieser Snippet initialisiert eine Liste mit dem Namen Quotes (Zitate), um die gescrapten Daten zu speichern. Anschließend werden alle HTML-Elemente des Zitats identifiziert und in einer Schleife durchlaufen, um den Text, den Autor und die Tags des Zitats zu extrahieren. Jedes extrahierte Zitat wird als Wörterbuch in der Liste Quotes (Zitate) gespeichert, wodurch die Daten für die spätere Verwendung oder den Export organisiert werden.

Super! Die Scraping-Logik ist nun implementiert.

Schritt Nr. 6: Export der gescrapten Daten

Verwenden Sie diese Codezeilen, um die gescrapten Daten in eine CSV-Datei zu exportieren:

# Open the file for export
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])
    
    # Write the header row
    writer.writeheader()
    
    # Write the scraped quotes data
    writer.writerows(quotes)

Das obige Snippet öffnet eine Datei mit dem Namen quotes.csv im Schreibmodus. Dann werden Spaltenüberschriften (TextAutorTags) erstellt, die Überschriften geschrieben und dann jedes Wörterbuch aus der Liste Quotes (Zitate) in die CSV-Datei geschrieben.

csv.DictWriter vereinfacht die Datenformatierung und erleichtert so die Speicherung strukturierter Daten. Damit es funktioniert, müssen Sie csv aus der Python-Standardbibliothek importieren:

import csv

Schritt Nr. 7: Das Ganze zusammensetzen

So sollte Ihr fertiges AIOHTTP-Web-Scraping-Skript aussehen:

import asyncio
import aiohttp
from bs4 import BeautifulSoup
import csv

# Define an asynchronous function to make the HTTP GET request
async def scrape_quotes():
    async with aiohttp.ClientSession() as session:
        async with session.get("http://quotes.toscrape.com") as response:
            # Access the HTML of the target page
            html = await response.text()

            # Parse the HTML content using BeautifulSoup
            soup = BeautifulSoup(html, "html.parser")

            # List to store the scraped data
            quotes = []

            # Extract all quotes from the page
            quote_elements = soup.find_all("div", class_="quote")

            # Loop through quotes and extract text, author, and tags
            for quote_element in quote_elements:
                text = quote_element.find("span", class_="text").get_text().replace("“", "").replace("”", "")
                author = quote_element.find("small", class_="author").get_text()
                tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

                # Store the scraped data
                quotes.append({
                    "text": text,
                    "author": author,
                    "tags": tags
                })

            # Open the file name for export
            with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
                writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])

                # Write the header row
                writer.writeheader()

                # Write the scraped quotes data
                writer.writerows(quotes)

# Run the asynchronous function
asyncio.run(scrape_quotes())

Sie können es ausführen mit:

python scraper.py

Oder, unter Linux/macOS:

python3 scraper.py

Eine quotes.csv-Datei wird im Stammordner Ihres Projekts erscheinen. Öffnen Sie sie und Sie werden Folgendes sehen:

Und voilà! Sie haben gerade gelernt, wie man Web Scraping mit AIOHTTP und BeautifulSoup durchführt.

AIOHTTP für Web Scraping: Erweiterte Funktionen und Techniken

Nachdem Sie nun wissen, wie Sie AIOHTTP für grundlegendes Web Scraping verwenden können, ist es an der Zeit, sich fortgeschrittenere Szenarien anzusehen.

In den folgenden Beispielen wird die Zielwebsite der HTTPBin.io /anything-Endpunkt sein. Dies ist eine praktische API, die die IP-Adresse, Header und andere vom Requester gesendete Daten zurückgibt.

Machen Sie sich bereit, AIOHTTP für Web Scraping zu beherrschen!

Benutzerdefinierte Header festlegen

Sie können benutzerdefinierte Header in einer AIOHTTP-Request mit dem Argument headers festlegen:

import aiohttp
import asyncio

async def fetch_with_custom_headers():
    # Custom headers for the request
    headers = {
        "Accept": "application/json",
        "Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,es-US;q=0.6,es;q=0.5,it-IT;q=0.4,it;q=0.3"
    }

    async with aiohttp.ClientSession() as session:
        # Make a GET request with custom headers
        async with session.get("https://httpbin.io/anything", headers=headers) as response:
            data = await response.json()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_headers())

Auf diese Weise stellt AIOHTTP eine GET-HTTP-Request mit den festgelegten Accept und Accept-LanguageHeadern.

Einrichtung eines benutzerdefinierten User-Agent

User-Agent ist einer der kritischsten HTTP-Header für Web Scraping. Standardmäßig verwendet AIOHTTP diesen User-Agent:

Python/<PYTHON_VERSION> aiohttp/<AIOHTTP_VERSION>

Der obige Standardwert kann Ihre Request leicht als von einem automatisierten Skript stammend entlarven. Dadurch erhöht sich das Risiko, von der Zielwebsite blockiert zu werden.

Um die Wahrscheinlichkeit, entdeckt zu werden, zu verringern, können Sie einen benutzerdefinierten User-Agent wie zuvor festlegen:

import aiohttp
import asyncio

async def fetch_with_custom_user_agent():
    # Define a Chrome-like custom User-Agent
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"
    }

    async with aiohttp.ClientSession(headers=headers) as session:
        # Make a GET request with the custom User-Agent
        async with session.get("https://httpbin.io/anything") as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_user_agent())

Entdecken Sie die besten User-Agents für Web Scraping!

Cookies setzen

Genau wie HTTP-Header können Sie benutzerdefinierte Cookies setzen, indem Sie die Cookies in ClientSession() verwenden:

import aiohttp
import asyncio

async def fetch_with_custom_cookies():
    # Define cookies as a dictionary
    cookies = {
        "session_id": "9412d7hdsa16hbda4347dagb",
        "user_preferences": "dark_mode=false"
    }

    async with aiohttp.ClientSession(cookies=cookies) as session:
        # Make a GET request with custom cookies
        async with session.get("https://httpbin.io/anything") as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_with_custom_cookies())

Mithilfe von Cookies können Sie die für Ihre Web-Scraping-Requests erforderlichen Sitzungsdaten aufnehmen.

Beachten Sie, dass die in ClientSession gesetzten Cookies für alle mit dieser Sitzung gemachten Requests gemeinsam genutzt werden. Um auf Sitzungscookies zuzugreifen, wenden Sie sich an ClientSession.cookie_jar.

Proxy-Integration

In AIOHTTP können Sie Ihre Requests über einen Proxy-Server leiten, um das Risiko von IP-Sperren zu verringern. Verwenden Sie dazu das Proxy-Argument in der HTTP-Methodenfunktion auf Sitzung:

import aiohttp
import asyncio

async def fetch_through_proxy():
    # Replace with the URL of your proxy server
    proxy_url = "<YOUR_PROXY_URL>"

    async with aiohttp.ClientSession() as session:
        # Make a GET request through the proxy server
        async with session.get("https://httpbin.io/anything", proxy=proxy_url) as response:
            data = await response.text()
            # Handle the response...
            print(data)

# Run the event loop
asyncio.run(fetch_through_proxy())

Wie Sie die Proxy-Authentifizierung und -Rotation durchführen können, erfahren Sie in unserer Anleitung Wie man einen Proxy in AIOHTTP verwendet.

Fehlerbehandlung

Standardmäßig gibt AIOHTTP nur bei Verbindungs- oder Netzwerkproblemen Fehler aus. Um Ausnahmen für HTTP-Responses auszulösen, wenn Sie 4xx– und  5xx-Statuscodes empfangen, können Sie einen der folgenden Ansätze verwenden:

  1. Setzen Sie raise_for_status=True beim Erstellen der ClientSession: Automatisches Auslösen von Ausnahmen für alle Requests, die über die Sitzung erfolgen, wenn der Response-Status 4xx oder 5xx ist.
  2. Übergeben Sie raise_for_status=True direkt an Request-Methoden: Aktivieren Sie die Fehlersuche für einzelne Request-Methoden (wie session.get() oder session.post()), ohne andere zu beeinträchtigen.
  3. Rufen Sie response.raise_for_status() manuell auf: Geben Sie die volle Kontrolle darüber, wann Ausnahmen ausgelöst werden, sodass Sie pro Request entscheiden können.

Option Nr. 1 – Beispiel:

import aiohttp
import asyncio

async def fetch_with_session_error_handling():
    async with aiohttp.ClientSession(raise_for_status=True) as session:
        try:
            async with session.get("https://httpbin.io/anything") as response:
                # No need to call response.raise_for_status(), as it is automatic
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_session_error_handling())

Wenn raise_for_status=True auf der Sitzungsebene gesetzt ist, werden alle Requests, die über diese Sitzung gemacht werden, einen aiohttp.ClientResponseError für 4xx– oder 5xx-Responses auslösen.

Option Nr. 2 – Beispiel:

import aiohttp
import asyncio

async def fetch_with_raise_for_status():
    async with aiohttp.ClientSession() as session:
        try:
            async with session.get("https://httpbin.io/anything", raise_for_status=True) as response:
                # No need to manually call response.raise_for_status(), it is automatic
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_raise_for_status())

In diesem Fall wird das Argument raise_for_status=True direkt an den Aufruf session.get() übergeben. Dadurch wird sichergestellt, dass automatisch eine Ausnahme für alle 4xx– oder 5xx-Statuscodes ausgelöst wird.

Option Nr. 3 – Beispiel:

import aiohttp
import asyncio

async def fetch_with_manual_error_handling():
    async with aiohttp.ClientSession() as session:
        try:
            async with session.get("https://httpbin.io/anything") as response:
                response.raise_for_status()  # Manually raises error for 4xx/5xx
                data = await response.text()
                print(data)
        except aiohttp.ClientResponseError as e:
            print(f"HTTP error occurred: {e.status} - {e.message}")
        except aiohttp.ClientError as e:
            print(f"Request error occurred: {e}")

# Run the event loop
asyncio.run(fetch_with_manual_error_handling())

Wenn Sie mehr Kontrolle über einzelne Requests wünschen, können Sie response.raise_for_status() manuell aufrufen, nachdem Sie eine Request gemacht haben. Auf diese Weise können Sie genau entscheiden, wann Sie Fehler angehen möchten.

Wiederholung fehlgeschlagener Requests

AIOHTTP bietet keine integrierte Unterstützung für die automatische Wiederholung von Requests. Um dies zu implementieren, müssen Sie benutzerdefinierte Logik oder eine Bibliothek eines Dritten wie aiohttp-retry verwenden. Damit können Sie eine Wiederholungslogik für fehlgeschlagene Requests konfigurieren, die bei vorübergehenden Netzwerkproblemen, Zeitüberschreitungen oder Ratenbeschränkungen hilft.

Installieren Sie aiohttp-retry mit:

pip install aiohttp-retry

Dann können Sie es wie folgt verwenden:

import asyncio
from aiohttp_retry import RetryClient, ExponentialRetry

async def main():
    retry_options = ExponentialRetry(attempts=1)
    retry_client = RetryClient(raise_for_status=False, retry_options=retry_options)
    async with retry_client.get("https://httpbin.io/anything") as response:
        print(response.status)
        
    await retry_client.close()

Dadurch wird das Wiederholungsverhalten mit einer exponentiellen Backoff-Strategie konfiguriert. Weitere Informationen finden Sie in den offiziellen Dokumenten.

AIOHTTP vs. Requests für Web Scraping

Nachfolgend eine zusammenfassende Tabelle zum Vergleich von AIOHTTP und Requets für Web Scraping:

Funktion AIOHTTP Requests
GitHub-Sterne 15.300 52.400
Kundenbetreuung ✔️ ✔️
Sync-Unterstützung ✔️
Async-Unterstützung ✔️
Server-Unterstützung ✔️
Verbindungspooling ✔️ ✔️
HTTP/2-Unterstützung
User-Agent-Anpassung ✔️ ✔️
Proxy-Unterstützung ✔️ ✔️
Cookie-Handhabung ✔️ ✔️
Wiederholungsmechanismus Nur über eine Bibliothek eines Dritten verfügbar Verfügbar über HTTPAdapters
Leistung Hoch Medium
Community-Unterstützung und -Beliebtheit Medium Groß

Einen vollständigen Vergleich finden Sie in unserem Blogbeitrag zu Requests vs. HTTPX vs. AIOHTTP.

Erfahren Sie, wie man mit HTTPX Websites scrapen kann.

Fazit

Durch diesen Artikel haben Sie erfahren, wie Sie die  aiohttp-Bibliothek für Web Scraping verwenden. Sie haben erkundet, was sie ist, welche Funktionen sie bietet und welchen Nutzen sie hat. AIOHTTP zeichnet sich als schnelle und zuverlässige Wahl für HTTP-Request bei der Erfassung von Online-Daten aus.

Bei automatisierten HTTP-Requests wird jedoch Ihre öffentliche IP-Adresse offengelegt. Das kann Ihre Identität und Ihren Standort preisgeben und Ihre Privatsphäre gefährden. Um Ihre Sicherheit und Privatsphäre zu schützen, ist eine der effektivsten Strategien die Verwendung eines Proxy-Servers, um Ihre IP-Adresse zu verbergen.

Bright Data kontrolliert die besten Proxy-Server der Welt und betreut Fortune-500-Unternehmen und mehr als 20.000 Kunden. Das Angebot umfasst eine breite Palette von Proxy-Arten:

  • Rechenzentrums-Proxys – über 770.000 Rechenzentrums-IPs.
  • Privatanwender-Proxys – über 72 Millionen Privatanwender-IPs in mehr als 195 Ländern.
  • ISP-Proxys – über 700.000 ISP-IPs.
  • Mobile Proxys – über 7 Millionen mobile IPs.

Erstellen Sie noch heute ein kostenloses Bright Data-Konto, um unsere Proxys und Scraping-Lösungen zu testen!

Keine Kreditkarte erforderlich