Die async/await-Syntax von Python spielt eine wichtige Rolle bei der einfachen Darstellung von asynchronen Prozessen, insbesondere bei I/O-gebundenen Aufgaben und Anwendungen, die viele Anfragen verarbeiten. In diesem Artikel werden die grundlegenden Konzepte dieser Syntax sowie praktische Anwendungen und Beispiele verständlich erklärt. Lernen Sie die Grundlagen der asynchronen Programmierung und vertiefen Sie Ihr Verständnis durch konkrete Code-Beispiele.
Grundkonzepte der async/await-Syntax
Die async/await-Syntax von Python sind Schlüsselwörter, die verwendet werden, um asynchrone Programmierung einfach umzusetzen. Mit ihrer Hilfe können langwierige Operationen (wie I/O-Operationen) effizient verarbeitet werden, wodurch die Reaktionsfähigkeit des Programms verbessert wird.
Was ist asynchrone Programmierung?
Asynchrone Programmierung ist eine Technik, bei der das Programm während des Wartens auf eine Aufgabe gleichzeitig andere Aufgaben ausführen kann. Während bei der synchronen Verarbeitung Aufgaben nacheinander ausgeführt werden, scheint bei der asynchronen Verarbeitung, dass mehrere Aufgaben „gleichzeitig“ ausgeführt werden.
Die Rolle von async und await
- async: Wird verwendet, um eine Funktion als asynchron zu definieren. Diese Funktion wird als Koroutine bezeichnet und kann mit
await
andere asynchrone Prozesse aufrufen. - await: Wird verwendet, um auf das Ergebnis eines asynchronen Prozesses zu warten. Während des Wartens mit
await
können andere Aufgaben ausgeführt werden, was die Effizienz des gesamten Programms verbessert.
Ein einfaches Beispiel
Hier ist ein einfaches Beispiel für die Verwendung von async/await:
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1) # 1 Sekunde warten
print("World")
# Ausführung der asynchronen Funktion
asyncio.run(say_hello())
Dieser Code gibt „Hello“ aus, wartet dann 1 Sekunde und gibt „World“ aus. Während der Wartezeit mit await
können andere asynchrone Aufgaben ausgeführt werden.
Merkmale von Koroutinen
- Mit
async
definierte Funktionen können nicht direkt ausgeführt werden und müssen mitawait
oderasyncio.run()
aufgerufen werden. - Um asynchrone Prozesse effizient zu nutzen, müssen Koroutinen und Aufgaben (die im nächsten Abschnitt behandelt werden) richtig kombiniert werden.
Übersicht und Rolle der asyncio-Bibliothek
Die asyncio
-Bibliothek von Python, die Teil der Standardbibliothek ist, bietet ein Set von Tools zur effizienten Verwaltung asynchroner Prozesse. Damit können I/O-Operationen und die gleichzeitige Ausführung mehrerer Aufgaben einfach umgesetzt werden.
Die Rolle von asyncio
- Verwaltung der Ereignisschleife: Sie spielt eine zentrale Rolle bei der Planung und Ausführung von Aufgaben.
- Verwaltung von Koroutinen und Aufgaben: Sie registriert asynchrone Prozesse als Aufgaben und führt sie effizient aus.
- Unterstützung für asynchrone I/O-Operationen: Sie führt Prozesse aus, die I/O-Wartezeiten wie Dateioperationen und Netzwerkkommunikation beinhalten.
Was ist eine Ereignisschleife?
Die Ereignisschleife ist eine Art Motor, der asynchrone Aufgaben der Reihe nach abarbeitet. In asyncio
verwaltet diese Schleife asynchrone Funktionen und sorgt für eine effiziente Planung der Aufgaben.
import asyncio
async def example_task():
print("Task started")
await asyncio.sleep(1)
print("Task finished")
async def main():
# Ausführung der Aufgabe innerhalb der Ereignisschleife
await example_task()
# Start der Ereignisschleife und Ausführung von main()
asyncio.run(main())
Wichtige asyncio-Funktionen und -Klassen
asyncio.run()
: Startet die Ereignisschleife und führt eine asynchrone Funktion aus.asyncio.create_task()
: Registriert eine Koroutine als Aufgabe in der Ereignisschleife.asyncio.sleep()
: Wartet asynchron für eine bestimmte Zeit.asyncio.gather()
: Führt mehrere Aufgaben gleichzeitig aus und sammelt die Ergebnisse.asyncio.Queue
: Eine Warteschlange, die es ermöglicht, Daten effizient zwischen asynchronen Aufgaben auszutauschen.
Ein einfaches Anwendungsbeispiel
Hier ist ein Beispiel, bei dem mehrere Aufgaben gleichzeitig ausgeführt werden:
async def task1():
print("Task 1 started")
await asyncio.sleep(2)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(1)
print("Task 2 finished")
async def main():
# Gleichzeitige Ausführung
await asyncio.gather(task1(), task2())
asyncio.run(main())
In diesem Programm werden Task 1 und Task 2 gleichzeitig ausgeführt, wobei Task 2 zuerst abgeschlossen wird.
Vorteile von asyncio
- Effiziente Verwaltung vieler Aufgaben.
- Leistungssteigerung bei I/O-gebundenen Aufgaben.
- Flexible Planung durch die Ereignisschleife.
Durch das Verständnis von asyncio können Sie das Potenzial der asynchronen Programmierung maximal ausschöpfen.
Unterschiede und Anwendung von Koroutinen und Aufgaben
Koroutinen und Aufgaben sind grundlegende Konzepte in der asynchronen Programmierung in Python. Durch das Verständnis ihrer Eigenschaften und Rollen können sie effizient eingesetzt werden, um asynchrone Prozesse umzusetzen.
Was ist eine Koroutine?
Eine Koroutine ist eine besondere Art von Funktion, die als asynchrone Funktion definiert wird. Sie wird mit async def
definiert und kann andere asynchrone Prozesse mit await
ausführen. Eine Koroutine kann während der Ausführung gestoppt und später wieder fortgesetzt werden.
Beispiel: Definition und Verwendung einer Koroutine
import asyncio
async def my_coroutine():
print("Start coroutine")
await asyncio.sleep(1)
print("End coroutine")
# Ausführung der Koroutine
asyncio.run(my_coroutine())
Was ist eine Aufgabe?
Eine Aufgabe ist eine Koroutine, die für die Ausführung in der Ereignisschleife verpackt wird. Sie wird mit asyncio.create_task()
erstellt und nach der Registrierung in der Ereignisschleife parallel ausgeführt.
Beispiel: Erstellen und Ausführen einer Aufgabe
import asyncio
async def my_coroutine(number):
print(f"Coroutine {number} started")
await asyncio.sleep(1)
print(f"Coroutine {number} finished")
async def main():
# Erstellen und gleichzeitige Ausführung mehrerer Aufgaben
task1 = asyncio.create_task(my_coroutine(1))
task2 = asyncio.create_task(my_coroutine(2))
# Warten auf den Abschluss der Aufgaben
await task1
await task2
asyncio.run(main())
In diesem Beispiel starten Task 1 und Task 2 gleichzeitig, und ihre Verarbeitung erfolgt parallel.
Unterschiede zwischen Koroutinen und Aufgaben
Merkmal | Koroutine | Aufgabe |
---|---|---|
Definierung | async def | asyncio.create_task() |
Ausführung | await oder asyncio.run() | Wird automatisch in der Ereignisschleife ausgeführt |
Gleichzeitige Ausführung | Schreibt eine einzelne asynchrone Aufgabe | Ermöglicht parallele Ausführung mehrerer asynchroner Aufgaben |
Tipps zur Verwendung
- Koroutinen werden verwendet, wenn einfache asynchrone Aufgaben geschrieben werden sollen.
- Aufgaben werden genutzt, wenn mehrere asynchrone Aufgaben parallel ausgeführt werden sollen.
Anwendungsbeispiel: Parallele Verarbeitung mit Aufgaben
Hier ein Beispiel, wie mehrere asynchrone Funktionen gleichzeitig ausgeführt werden:
import asyncio
async def fetch_data(url):
print(f"Fetching data from {url}")
await asyncio.sleep(2) # Simulierte Netzwerkwartezeit
print(f"Finished fetching data from {url}")
async def main():
urls = ["https://example.com", "https://example.org", "https://example.net"]
# Erstellen mehrerer Aufgaben
tasks = [asyncio.create_task(fetch_data(url)) for url in urls]
# Warten auf den Abschluss aller Aufgaben
await asyncio.gather(*tasks)
asyncio.run(main())
In diesem Programm werden mit List Comprehension mehrere Aufgaben erzeugt, die parallel ausgeführt werden.
Wichtige Hinweise
- Die Ausführungsreihenfolge von Aufgaben ist nicht garantiert, daher ist sie nicht für abhängige Prozesse geeignet.
- Aufgaben können nur innerhalb der Ereignisschleife verwendet werden, nicht außerhalb der Schleife.
Durch das korrekte Verständnis der Unterschiede zwischen Koroutinen und Aufgaben können Sie asynchrone Programme effizient gestalten und optimieren.
Vorteile und Grenzen der asynchronen Verarbeitung
Asynchrone Verarbeitung ist besonders bei Anwendungen, die viele I/O-Operationen ausführen, ein wertvolles Werkzeug zur Leistungssteigerung, jedoch ist sie nicht überall einsetzbar. In diesem Abschnitt werden die Vorteile und Grenzen der asynchronen Verarbeitung erläutert, damit Sie sie gezielt einsetzen können.
Vorteile der asynchronen Verarbeitung
1. Geschwindigkeit und Effizienz
- Nutzung von Ressourcen während der I/O-Wartezeiten: Während bei synchroner Verarbeitung das Programm während der I/O-Wartezeit stoppt, können bei asynchroner Verarbeitung andere Aufgaben ausgeführt werden, sodass Ressourcen effizienter genutzt werden.
- Hohe Durchsatzleistung: Ideal für Server, die viele Anfragen gleichzeitig verarbeiten, oder Clients, die zahlreiche Netzwerkoperationen parallel durchführen.
2. Verbesserung der Reaktionsfähigkeit
- Verbesserung der Benutzererfahrung: Asynchrone Verarbeitung ermöglicht es, Hintergrundaufgaben auszuführen, ohne die Benutzeroberfläche zu blockieren, wodurch die Reaktionsfähigkeit verbessert wird.
- Reduzierung der Wartezeiten: Durch die Verwendung asynchroner I/O können andere Prozesse parallel ablaufen, wodurch die Gesamtladezeit verkürzt wird.
3. Flexibilität und Skalierbarkeit
- Skalierbare Architektur: Asynchrone Programme verbrauchen keine übermäßigen Threads oder Prozesse und nutzen die Systemressourcen effizient.
- Multitasking: Asynchrone Aufgaben können effizient zwischen den Aufgaben wechseln, sodass das System auch bei hoher Last stabil bleibt.
Grenzen der asynchronen Verarbeitung
1. Komplexität des Programms
Asynchrone Verarbeitung kann schwieriger zu verstehen und zu debuggen sein als synchrone Prozesse. Besonders in folgenden Bereichen können Probleme auftreten:
- Rennbedingungen: Wenn mehrere Aufgaben auf dieselbe Ressource zugreifen, kann es schwierig sein, die Datenintegrität zu wahren.
- Callback-Hölle: Bei komplexen Abhängigkeiten in asynchronen Prozessen kann der Code schwer lesbar werden.
2. Ineffizienz bei CPU-gebundenen Aufgaben
Asynchrone Verarbeitung ist hauptsächlich für I/O-gebundene Aufgaben optimiert. Bei rechenintensiven CPU-gebundenen Aufgaben kann es aufgrund von Einschränkungen wie dem GIL (Global Interpreter Lock) zu keinen Leistungssteigerungen kommen.
3. Notwendigkeit einer geeigneten Architektur
Um asynchrone Programme effektiv umzusetzen, ist eine angemessene Architektur und die Wahl der richtigen Bibliotheken erforderlich. Schlechte Designentscheidungen können zu Problemen führen:
- Deadlocks: Aufgaben, die aufeinander warten und somit in einem Stillstand enden.
- Fehlerhafte Planung: Ineffiziente Planung kann dazu führen, dass die Ausführung länger dauert als erwartet.
Tipps zur Nutzung der asynchronen Verarbeitung
1. Gezielter Einsatz
- Für I/O-gebundene Aufgaben verwenden: Ideal für Datenbankoperationen, Netzwerkkommunikation, Dateioperationen und ähnliche Aufgaben.
- CPU-gebundene Aufgaben mit Threads oder Prozessen behandeln: Kombinieren Sie asynchrone und parallele Verarbeitungstechniken.
2. Verwendung hochwertiger Tools und Bibliotheken
- asyncio: Ein grundlegendes Werkzeug in der Standardbibliothek zur Verwaltung asynchroner Prozesse.
- aiohttp: Eine Bibliothek für asynchrone HTTP-Kommunikation.
- Quart und FastAPI: Asynchron unterstützte Web-Frameworks.
3. Umfassendes Debugging und Monitoring
- Verwenden Sie Logs, um das Verhalten zwischen Aufgaben zu überwachen und beim Debuggen zu unterstützen.
- Aktivieren Sie den Debugging-Modus von
asyncio
, um detaillierte Fehlermeldungen zu erhalten.
Die asynchrone Verarbeitung kann die Leistung von Anwendungen erheblich steigern, wenn sie richtig entworfen und eingesetzt wird. Gleichzeitig ist es wichtig, ihre Grenzen zu verstehen und eine geeignete Architektur zu wählen.
Erstellen von asynchronen Funktionen in der Praxis
Um asynchrone Prozesse in Python umzusetzen, kombinieren wir async
und await
, um asynchrone Funktionen zu definieren und auszuführen. In diesem Abschnitt lernen wir, wie man asynchrone Funktionen erstellt und die grundlegenden Abläufe der asynchronen Verarbeitung umsetzt.
Grundstruktur einer asynchronen Funktion
Eine asynchrone Funktion wird mit async def
definiert. Innerhalb dieser Funktion verwenden wir await
, um andere asynchrone Prozesse aufzurufen.
Beispiel einer einfachen asynchronen Funktion
import asyncio
async def greet():
print("Hello,")
await asyncio.sleep(1) # Asynchrone 1 Sekunde warten
print("World!")
# Asynchrone Funktion ausführen
asyncio.run(greet())
In diesem Beispiel wartet await asyncio.sleep(1)
asynchron für 1 Sekunde. Während dieser Wartezeit können andere Aufgaben fortgesetzt werden.
Verknüpfung asynchroner Funktionen
Es ist auch möglich, mehrere asynchrone Funktionen zu verknüpfen und Aufgaben miteinander zu koordinieren.
Beispiel zur Verknüpfung asynchroner Funktionen
async def task1():
print("Task 1 started")
await asyncio.sleep(2)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(1)
print("Task 2 finished")
async def main():
# Asynchrone Funktionen nacheinander ausführen
await task1()
await task2()
asyncio.run(main())
Hier wird die Funktion main
als asynchrone Funktion definiert, die die asynchronen Funktionen task1
und task2
der Reihe nach ausführt.
Asynchrone Funktionen und parallele Verarbeitung
Um asynchrone Funktionen parallel auszuführen, verwenden wir asyncio.create_task
. Dadurch können mehrere asynchrone Aufgaben gleichzeitig ausgeführt werden.
Beispiel zur parallelen Verarbeitung
async def task1():
print("Task 1 started")
await asyncio.sleep(2)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(1)
print("Task 2 finished")
async def main():
# Erstellen von Aufgaben für parallele Ausführung
task1_coroutine = asyncio.create_task(task1())
task2_coroutine = asyncio.create_task(task2())
# Warten auf den Abschluss beider Aufgaben
await task1_coroutine
await task2_coroutine
asyncio.run(main())
In diesem Beispiel werden task1
und task2
parallel ausgeführt. Task 2 wird nach 1 Sekunde abgeschlossen, danach endet Task 1.
Anwendungsbeispiel: Ein einfacher asynchroner Zähler
Hier ist ein Beispiel, bei dem mehrere Zähler gleichzeitig laufen. Dies wird mit asynchronen Funktionen erreicht.
async def count(number):
for i in range(1, 4):
print(f"Counter {number}: {i}")
await asyncio.sleep(1) # Asynchrone 1 Sekunde warten
async def main():
# Mehrere Zähler parallel ausführen
await asyncio.gather(count(1), count(2), count(3))
asyncio.run(main())
Ausgabe
Counter 1: 1
Counter 2: 1
Counter 3: 1
Counter 1: 2
Counter 2: 2
Counter 3: 2
Counter 1: 3
Counter 2: 3
Counter 3: 3
Durch die Verwendung asynchroner Verarbeitung wird deutlich, dass jeder Zähler unabhängig voneinander läuft.
Wichtige Punkte und Hinweise
- Asynchrone Verarbeitung reduziert den Ressourcenverbrauch und ermöglicht eine effiziente Verwaltung von Aufgaben.
- Verwenden Sie
asyncio.gather
oderasyncio.create_task
, je nach Bedarf. - Führen Sie asynchrone Funktionen mit
asyncio.run
oder der Ereignisschleife aus.
Durch das Üben mit grundlegenden asynchronen Funktionen können Sie Ihre Fähigkeiten in der asynchronen Programmierung verbessern.
Methoden zur Umsetzung der Parallelverarbeitung: Verwendung von gather und wait
In der asynchronen Verarbeitung mit Python werden asyncio.gather
und asyncio.wait
verwendet, um mehrere Aufgaben effizient parallel auszuführen. Durch das Verständnis ihrer Merkmale und Anwendungsmöglichkeiten können flexiblere asynchrone Programme erstellt werden.
Überblick und Anwendungsbeispiel von asyncio.gather
asyncio.gather
führt mehrere asynchrone Aufgaben zusammen aus und wartet, bis alle Aufgaben abgeschlossen sind. Nach Abschluss gibt es die Ergebnisse jeder Aufgabe als Liste zurück.
Beispiel
import asyncio
async def task1():
await asyncio.sleep(1)
return "Task 1 complete"
async def task2():
await asyncio.sleep(2)
return "Task 2 complete"
async def main():
results = await asyncio.gather(task1(), task2())
print(results)
asyncio.run(main())
Ergebnis der Ausführung
['Task 1 complete', 'Task 2 complete']
Merkmale
- Wartet auf den Abschluss der parallel ausgeführten Aufgaben und gibt die Ergebnisse als Liste zurück.
- Im Falle einer Ausnahme stoppt
gather
alle Aufgaben und propagiert die Ausnahme an den Aufrufer.
Überblick und Anwendungsbeispiel von asyncio.wait
asyncio.wait
führt mehrere Aufgaben parallel aus und gibt ein Set von abgeschlossenen und ausstehenden Aufgaben zurück.
Beispiel
import asyncio
async def task1():
await asyncio.sleep(1)
print("Task 1 complete")
async def task2():
await asyncio.sleep(2)
print("Task 2 complete")
async def main():
tasks = [task1(), task2()]
done, pending = await asyncio.wait(tasks)
print(f"Done tasks: {len(done)}, Pending tasks: {len(pending)}")
asyncio.run(main())
Ergebnis der Ausführung
Task 1 complete
Task 2 complete
Done tasks: 2, Pending tasks: 0
Merkmale
- Ermöglicht es, den Status der Aufgaben (abgeschlossen oder ausstehend) detailliert zu überprüfen.
- Auch wenn eine Aufgabe frühzeitig abgeschlossen wird, können ausstehende Aufgaben weiter verarbeitet werden.
- Mit der
return_when
-Option vonasyncio.wait
können Sie das Ende der Aufgaben unter bestimmten Bedingungen steuern.
Beispiel für die return_when-Option
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
FIRST_COMPLETED
: Gibt zurück, wenn die erste Aufgabe abgeschlossen ist.FIRST_EXCEPTION
: Gibt zurück, wenn die erste Ausnahme auftritt.ALL_COMPLETED
: Wartet, bis alle Aufgaben abgeschlossen sind (Standard).
Unterschiedliche Verwendung von gather und wait
- Wenn Sie die Ergebnisse gebündelt erhalten möchten: Verwenden Sie
asyncio.gather
. - Wenn Sie den Status jeder Aufgabe separat verwalten möchten: Verwenden Sie
asyncio.wait
. - Wenn Sie Aufgaben vorzeitig beenden oder Ausnahmen behandeln möchten:
asyncio.wait
ist geeignet.
Anwendungsbeispiel: Parallelabruf von APIs
Das folgende Beispiel zeigt, wie mehrere APIs parallel abgerufen und die Antworten erhalten werden:
import asyncio
async def fetch_data(api_name, delay):
print(f"Fetching from {api_name}...")
await asyncio.sleep(delay) # Simulierte Wartezeit
return f"Data from {api_name}"
async def main():
apis = [("API_1", 2), ("API_2", 1), ("API_3", 3)]
tasks = [fetch_data(api, delay) for api, delay in apis]
# Parallelverarbeitung mit gather und Ergebnissammlung
results = await asyncio.gather(*tasks)
for result in results:
print(result)
asyncio.run(main())
Ergebnis der Ausführung
Fetching from API_1...
Fetching from API_2...
Fetching from API_3...
Data from API_2
Data from API_1
Data from API_3
Wichtige Hinweise
- Ausnahmebehandlung: Wenn bei parallelen Aufgaben Ausnahmen auftreten, müssen diese korrekt abgefangen und behandelt werden. Nutzen Sie
try
/except
. - Aufgabenstornierung
: Wenn Aufgaben nicht mehr benötigt werden, verwenden Sie
task.cancel()
zur Stornierung. - Achten Sie auf Deadlocks: Es ist wichtig, eine Architektur zu entwerfen, die gegenseitige Wartebedingungen vermeidet.
Durch den effektiven Einsatz von asyncio.gather
und asyncio.wait
können die Flexibilität und Effizienz der asynchronen Verarbeitung maximiert werden.
Beispiel für asynchrones I/O: Datei- und Netzwerkoperationen
Asynchrones I/O ist eine Methode zur Effizienzsteigerung bei Operationen, die auf Wartezeiten angewiesen sind, wie Dateioperationen und Netzwerkkommunikation. Durch die Nutzung von asyncio
können solche asynchronen I/O-Operationen einfach implementiert werden. In diesem Abschnitt werden die grundlegenden Anwendungen von asynchronem I/O anhand konkreter Beispiele erklärt.
Asynchrone Dateioperationen
Für asynchrone Dateioperationen wird die Bibliothek aiofiles
verwendet. Diese Bibliothek erweitert die Standardbibliothek, sodass Dateioperationen asynchron durchgeführt werden können.
Beispiel: Asynchrone Dateioperationen
import aiofiles
import asyncio
async def read_file(filepath):
async with aiofiles.open(filepath, mode='r') as file:
contents = await file.read()
print(f"Contents of {filepath}:")
print(contents)
async def write_file(filepath, data):
async with aiofiles.open(filepath, mode='w') as file:
await file.write(data)
print(f"Data written to {filepath}")
async def main():
filepath = 'example.txt'
await write_file(filepath, "Hello, Async File IO!")
await read_file(filepath)
asyncio.run(main())
Wichtige Punkte
- Mit
aiofiles.open
können Sie Dateien asynchron bearbeiten. - Verwenden Sie die
async with
-Syntax, um Dateien sicher zu handhaben. - Während Dateioperationen fortschreiten, können andere Aufgaben gleichzeitig durchgeführt werden.
Asynchrone Netzwerkoperationen
Für Netzwerkoperationen kann die Bibliothek aiohttp
verwendet werden, um asynchrone HTTP-Anfragen zu stellen.
Beispiel: Asynchrone HTTP-Anfragen
import aiohttp
import asyncio
async def fetch_url(session, url):
async with session.get(url) as response:
print(f"Fetching {url}")
content = await response.text()
print(f"Content from {url}: {content[:100]}...")
async def main():
urls = [
"https://example.com",
"https://example.org",
"https://example.net"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
await asyncio.gather(*tasks)
asyncio.run(main())
Wichtige Punkte
- Verwenden Sie
aiohttp.ClientSession
für asynchrone HTTP-Kommunikation. - Mit der
async with
-Syntax verwalten Sie die Sitzung und senden Anfragen sicher. - Durch parallele Anfragen mit
asyncio.gather
wird die Effizienz gesteigert.
Kombination von asynchronem Datei- und Netzwerk-I/O
Durch die Kombination von asynchronen Datei- und Netzwerkoperationen können Daten effizient gesammelt und gespeichert werden.
Beispiel: Speichern von heruntergeladenen Daten asynchron
import aiohttp
import aiofiles
import asyncio
async def fetch_and_save(session, url, filepath):
async with session.get(url) as response:
print(f"Fetching {url}")
content = await response.text()
async with aiofiles.open(filepath, mode='w') as file:
await file.write(content)
print(f"Content from {url} saved to {filepath}")
async def main():
urls = [
("https://example.com", "example_com.txt"),
("https://example.org", "example_org.txt"),
("https://example.net", "example_net.txt")
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_and_save(session, url, filepath) for url, filepath in urls]
await asyncio.gather(*tasks)
asyncio.run(main())
Beispielergebnis
- Der Inhalt von
https://example.com
wird in der Dateiexample_com.txt
gespeichert. - Der Inhalt der anderen URLs wird ebenfalls in die entsprechenden Dateien gespeichert.
Wichtige Hinweise zum Asynchronen I/O
- Implementierung der Ausnahmebehandlung
Bereiten Sie sich auf Netzwerkfehler und Datei-Schreibfehler vor, indem Sie geeignete Ausnahmebehandlung durchführen.
try:
# Asynchrone Aufgaben
except Exception as e:
print(f"An error occurred: {e}")
- Implementierung der Drosselung
Bei der gleichzeitigen Ausführung vieler asynchroner Aufgaben kann dies die System- oder Serverlast erhöhen. Mitasyncio.Semaphore
können Sie die Anzahl der gleichzeitig ausgeführten Aufgaben begrenzen.
semaphore = asyncio.Semaphore(5) # Maximale Anzahl paralleler Aufgaben
async with semaphore:
await some_async_task()
- Timeout-Implementierung
Um Prozesse ohne Antwort zu verhindern, setzen Sie ein Timeout.
try:
await asyncio.wait_for(some_async_task(), timeout=10)
except asyncio.TimeoutError:
print("Task timed out")
Durch den richtigen Einsatz von asynchronem I/O können Sie die Effizienz und den Durchsatz Ihrer Anwendungen erheblich steigern.
Anwendungsbeispiel: Aufbau eines asynchronen Web-Crawlers
Mit asynchroner Verarbeitung können Sie einen schnellen und effizienten Web-Crawler erstellen. Mit asynchronem I/O können Sie viele Webseiten parallel abrufen und die Crawling-Geschwindigkeit maximieren. In diesem Abschnitt zeigen wir ein Beispiel für die Implementierung eines asynchronen Web-Crawlers mit Python.
Grundstruktur eines asynchronen Web-Crawlers
Ein asynchroner Web-Crawler besteht aus drei wichtigen Elementen:
- Verwaltung der URL-Liste: Effiziente Verwaltung der URLs, die gecrawlt werden sollen.
- Asynchrone HTTP-Kommunikation: Abrufen von Webseiten mit der asynchronen Bibliothek
aiohttp
. - Speichern der Daten: Speichern der abgerufenen Daten mit asynchronen Dateioperationen.
Codebeispiel: Asynchroner Web-Crawler
Das folgende Beispiel zeigt den grundlegenden Aufbau eines asynchronen Web-Crawlers:
import aiohttp
import aiofiles
import asyncio
from bs4 import BeautifulSoup
async def fetch_page(session, url):
try:
async with session.get(url) as response:
if response.status == 200:
html = await response.text()
print(f"Fetched {url}")
return html
else:
print(f"Failed to fetch {url}: {response.status}")
return None
except Exception as e:
print(f"Error fetching {url}: {e}")
return None
async def parse_and_save(html, url, filepath):
if html:
soup = BeautifulSoup(html, 'html.parser')
title = soup.title.string if soup.title else "No Title"
async with aiofiles.open(filepath, mode='a') as file:
await file.write(f"URL: {url}\nTitle: {title}\n\n")
print(f"Saved data for {url}")
async def crawl(urls, output_file):
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
tasks.append(process_url(session, url, output_file))
await asyncio.gather(*tasks)
async def process_url(session, url, output_file):
html = await fetch_page(session, url)
await parse_and_save(html, url, output_file)
async def main():
urls = [
"https://example.com",
"https://example.org",
"https://example.net"
]
output_file = "crawl_results.txt"
# Initialisierung: Leeren der Ergebnisdatei
async with aiofiles.open(output_file, mode='w') as file:
await file.write("")
await crawl(urls, output_file)
asyncio.run(main())
Erklärung der Codeausführung
fetch_page
-Funktion
Führt eine asynchrone HTTP-Anfrage durch, um die HTML-Seite abzurufen. Überprüft den Statuscode und behandelt Fehler.parse_and_save
-Funktion
Verwendet BeautifulSoup, um das HTML zu parsen und den Titel der Seite zu extrahieren. Speichert diese Daten asynchron in einer Datei.crawl
-Funktion
Verarbeitet die URL-Liste und führt die URLs parallel aus. Verwendetasyncio.gather
, um die Aufgaben zu bündeln.process_url
-Funktion
Kapselt die vollständige Verarbeitung einer URL mitfetch_page
undparse_and_save
.
Beispiel für die Ergebnisse der Ausführung
Die Datei crawl_results.txt
wird die folgenden Daten enthalten:
URL: https://example.com
Title: Example Domain
URL: https://example.org
Title: Example Domain
URL: https://example.net
Title: Example Domain
Leistungsoptimierung
- Begrenzung der parallelen Aufgaben
Wenn viele URLs gecrawlt werden, begrenzen Sie die Anzahl paralleler Aufgaben, um die Serverlast zu verringern.
semaphore = asyncio.Semaphore(10)
async def limited_process_url(semaphore, session, url, output_file):
async with semaphore:
await process_url(session, url, output_file)
- Hinzufügen einer Retry-Funktion
Durch die Implementierung einer Logik zur Wiederholung von fehlgeschlagenen Anfragen können Sie die Zuverlässigkeit erhöhen.
Wichtige Hinweise
- Überprüfung der Legalität
Wenn Sie einen Web-Crawler betreiben, stellen Sie sicher, dass Sie dierobots.txt
und die Nutzungsbedingungen der Zielwebseite einhalten. - Ausnahmebehandlung
Behandeln Sie Netzwerkfehler und HTML-Parsing-Fehler korrekt, um den Betrieb des Crawlers nicht zu stoppen. - Timeout-Implementierung
Setzen Sie ein Timeout für Anfragen, um endloses Warten zu vermeiden.
async with session.get(url, timeout=10) as response:
Ein asynchroner Web-Crawler ermöglicht mit der richtigen Gestaltung und Kontrolle eine effiziente und skalierbare Datensammlung.
Zusammenfassung
In diesem Artikel haben wir die asynchrone Verarbeitung mit Python unter Verwendung der async/await
-Syntax detailliert behandelt, von den Grundlagen bis hin zu fortgeschrittenen Anwendungen. Das Verständnis der asynchronen Verarbeitung ermöglicht es, I/O-intensive Aufgaben effizienter zu gestalten und die Leistung von Anwendungen zu steigern.
Besonders die Grundlagen der asyncio
-Bibliothek, die parallele Verarbeitung mit gather
und wait
, konkrete Beispiele für asynchrones I/O und die Implementierung eines asynchronen Web-Crawlers haben uns praktische Fähigkeiten vermittelt.
Asynchrone Programmierung unterstützt den Aufbau effizienter und skalierbarer Systeme, erfordert jedoch sorgfältige Ausnahmebehandlung und rechtliche Überlegungen. Nutzen Sie diesen Artikel als Referenz, um Ihre Fähigkeiten in der asynchronen Verarbeitung zu erweitern und anzuwenden.