Website-Suche

Wie man HTTP-Anfragen mit Python durchführt - Teil 2 - Die Anforderungsbibliothek


Im vorherigen Artikel haben wir gesehen, wie man grundlegende HTTP-Anfragen mit der python3-Standardbibliothek ausführt. Wenn Anforderungen komplexer werden oder wir einfach weniger Code verwenden möchten und es uns nichts ausmacht, unserem Projekt eine Abhängigkeit hinzuzufügen, ist es möglich (und manchmal sogar empfohlen), das Modul für externe Anfragen zu verwenden. Die Bibliothek, die sich das Motto "HTTP for Humans" zu eigen gemacht hat, steht im Mittelpunkt dieses Artikels.

In diesem Tutorial lernen Sie:

  • Wie man HTTP-Anfragen mit python3 und der 'requests'-Bibliothek durchführt
  • Verwalten von Serverantworten
  • So arbeiten Sie mit Sitzungen

HTTP-Anfragen mit Python – Teil II: Die Anforderungsbibliothek

Verwendete Softwareanforderungen und Konventionen

Ausführen von Anfragen mit der Bibliothek "requests"

Im ersten Teil dieser Serie haben wir grundlegende HTTP-Anfragen nur mit der Standardbibliothek ausgeführt. Wenn Anfragen komplexer werden, z. B. wenn wir Cookies zwischen einer Anfrage und einer anderen aufbewahren müssen, können wir die externe Bibliothek für Anfragen verwenden, die unsere Arbeit vereinfacht und viele Operationen unter der Haube für uns ausführt. Da die Bibliothek nicht in einer Standard-Python3-Installation enthalten ist, müssen wir sie auf unserem System installieren, bevor wir sie verwenden können. Eine distributionsunabhängige Methode, um diese Aufgabe zu erfüllen, ist die Verwendung von pip, dem Python-Paketmanager:

pip3 install requests --user

Nachdem wir die Bibliothek installiert haben, sehen wir uns einige Beispiele für deren Verwendung an.

Ausführen einer get-Anforderung

Erinnern Sie sich an die Anfrage, die wir mit den NASA-APIs gestellt haben, um das "Bild des Tages" für ein bestimmtes Datum abzurufen? Für das Erstellen und Senden derselben Anforderung mit der Anforderungsbibliothek ist nur eine Codezeile erforderlich:

>>> import requests
>>> response = requests.get("https://api.nasa.gov/planetary/apod", params={"api_key": "DEMO_KEY", "date": "2019-04-11"})

Wir haben die URL und die Abfrageparameter (immer noch als Dictionary) jeweils als erstes und zweites Argument der get-Funktion übergeben. Was gibt diese Funktion zurück? Sie gibt eine Instanz der requests.models.Response-Klasse zurück. Die Interaktion mit Instanzen dieser Klasse ist sehr einfach. Möchten wir den json-codierten Inhalt der Antwort abrufen? Leicht! Wir müssen nur die JSON-Methode des Objekts aufrufen:

>>> response.json()
{'date': '2019-04-11',
'explanation': 'What does a black hole look like? To find out, radio '
                'telescopes from around the Earth coordinated observations of '
                'black holes with the largest known event horizons on the '
                ...
                'immediate vicinity of the black hole in the center of our '
                'Milky Way Galaxy.',
'hdurl': 'https://apod.nasa.gov/apod/image/1904/M87bh_EHT_2629.jpg',
'media_type': 'image',
'service_version': 'v1',
'title': 'First Horizon-Scale Image of a Black Hole',
'url': 'https://apod.nasa.gov/apod/image/1904/M87bh_EHT_960.jpg'}

Möchten wir die Antwort des Servers als String erhalten? Alles, was wir tun müssen, ist, auf die Texteigenschaft zuzugreifen:

response.text

Auf die gleiche Weise können wir auf den Grund, die status_code und die Header der Anfrage zugreifen. Wir müssen nur auf die jeweiligen Eigenschaften zugreifen:

>>> response.reason
'OK'
>>> response.status_code
200
>>> response.headers
{'Server': 'openresty', 'Date': 'Thu, 18 Apr 2019 10:46:26 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'X-RateLimit-Limit': '40', 'X-RateLimit-Remaining': '39', 'Via': '1.1 vegur, http/1.1 api-umbrella (ApacheTrafficServer [cMsSf ])', 'Age': '0', 'X-Cache': 'MISS', 'Access-Control-Allow-Origin': '*', 'Strict-Transport-Security': 'max-age=31536000; preload', 'Content-Encoding': 'gzip'}

Herunterladen einer Datei

Das Herunterladen einer Datei ist ebenfalls sehr einfach. Zuerst müssen wir den Stream-Parameter der get-Funktion verwenden. Standardmäßig ist dieser Parameter auf False festgelegt, was bedeutet, dass der Text der Antwort sofort heruntergeladen wird. Da wir möglicherweise eine große Datei herunterladen möchten, möchten wir sie auf True setzen: Auf diese Weise werden nur die Header der Antwort sofort heruntergeladen und die Verbindung bleibt offen, damit wir sie nach Belieben weiterverarbeiten können:

>>> latest_kernel_tarball = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.7.tar.xz"
>>> with requests.get(latest_kernel_tarball, stream=True) as response:
...    with open("latest-kernel.tar.xz", "wb") as tarball:
...        for chunk in response.iter_content(16384):
...            tarball.write(chunk)

Der Code ähnelt seinem Gegenstück in der Standardbibliothek: Was sich geändert hat, ist die Verwendung der iter_content Methode des Antwortobjekts. Im vorherigen Beispiel haben wir innerhalb einer while-Schleife gearbeitet, die wir nur unterbrochen haben, wenn der Inhalt der Antwort konsumiert wurde. Mit dieser Methode können wir auf elegantere Weise in die Zieldatei schreiben, da wir den Inhalt der Antwort iterieren können. Die iter_content-Methode akzeptiert das optionale Argument chunk_size, eine ganze Zahl, die die Blockgröße in Bytes angibt (die Daten, die bei jeder Iteration in den Arbeitsspeicher gelesen werden sollen).

Senden von formularcodierten Daten oder JSON in einer Anforderung

Das Senden von formularcodierten Daten (z. B. in einer POST-Anforderung) mit der Bibliothek "requests" erfordert weniger Code als derselbe Vorgang, der nur mit der Standardbibliothek ausgeführt wird:

>>>request_data = {
...    "variable1": "value1",
...    "variable2": "value2"
...}
>>>response = requests.post("https://httpbin.org/post", data=request_data)

Um die gleichen Daten zu übergeben, aber als json:

response = requests.post("https://httpbin.org/post", json=request_data)

Durch die Verwendung des json-Parameters der Funktion müssen wir uns nicht einmal um die Codierung der Zeichenkette mit json.dumps kümmern: Sie wird für die Verwendung unter der Haube durchgeführt.

Hochladen einer Datei

Das Hochladen einer Datei mit der Standardbibliothek kann eine sehr mühsame Aufgabe sein, aber es ist sehr einfach, die Anforderungsbibliothek zu verwenden. Angenommen, wir möchten ein Bild hochladen:

>>> response = requests.post(
...    "https://httpbin.org/post", files={'file': open('nasa_black_hole.png', 'rb')})

Beeindruckend kurzer Code! Wir haben eine Post-Anfrage durchgeführt, diesmal mit dem Argument files. Bei diesem Argument muss es sich um ein Wörterbuch handeln, bei dem der Schlüssel das Feld "name" und der Wert ein Dateiobjekt ist, das in diesem Fall von der open-Funktion zurückgegeben wird.

Was ist mit den anderen HTTP-Verben? Jede von ihnen wird mit der entsprechend benannten Funktion verwendet: put, delete, head oder options. Alle von ihnen können im Grunde mit der gleichen Benutzeroberfläche verwendet werden, die wir zuvor gesehen haben.

Arbeiten mit Sitzungen

Die Anforderungsbibliothek ermöglicht es uns, Sitzungen zu verwenden: Wenn Anfragen aus einem Sitzungskontext gesendet werden, werden Cookies zwischen einer Anfrage und einer anderen gespeichert. Dies ist die empfohlene Methode, um mehrere Anfragen an denselben Host zu stellen, da auch die gleiche TCP-Verbindung wiederverwendet wird. Sehen wir uns an, wie Sie eine Sitzung erstellen und eine Anfrage damit senden:

>>> session = requests.Session()
>>> response = session.get("https://httpbin.org/cookies/set?lastname=skywalker")

Wir haben eine Instanz der Anforderungen erstellt. Session-Klasse, und anstatt eine Anforderung selbst auszuführen, wie wir es in den vorherigen Beispielen getan haben, haben wir die Methode verwendet, die nach dem HTTP-Verb benannt ist (in diesem Fall get), das auf die gleiche Weise verwendet wird. Die Anforderungs-URL war diesmal http://httpbin.org/cookies/set, ein Endpunkt, mit dem wir die Cookie-Parameter, die wir senden, in der Abfragezeichenfolge festlegen konnten. Der von uns getätigte Aufruf setzte ein Cookie, das nun in der Sitzung gespeichert wird und in allen Anfragen verwendet wird, die aus dem Sitzungskontext gesendet werden. Um alle Cookies aufzulisten, die mit einer Sitzung verbunden sind, können wir auf die Eigenschaft cookies zugreifen, die eine Instanz der Klasse requests.cookies.RequestsCookieJar' ist:

>>> session.cookies
<RequestsCookieJar[Cookie(version=0, name='lastname', value='skywalker', port=None, port_specified=False, domain='httpbin.org', domain_specified=False,
domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={}, rfc2109=False)]>
>>> # Access the cookies keys
...session.cookies.keys()
['lastname']
>>>
>>> # Access the cookies values
...session.cookies.values()
['skywalker']
>>>
>>> # The iterkeys method returns an iterator of names of cookies
...session.cookies.iterkeys()
<generator object RequestsCookieJar.iterkeys at 0x7f414a1a5408>
>>> # The itervalues method does the same but for values
...session.cookies.itervalues()
<generator object RequestsCookieJar.itervalues at 0x7f414a1a5408>

Um gespeicherte Cookies in der Sitzung zu bereinigen, können wir die Clear-Methode verwenden:

>>> session.cookies.clear()
>>> session.cookies
<RequestsCookieJar[]>

Erstellen eines Anforderungsobjekts

Bisher haben wir nur Funktionen wie get, post oder put verwendet, die im Grunde genommen Anfragen "on the fly" erstellen und senden. Es gibt Fälle, in denen wir ein Request-Objekt erstellen möchten, es aber nicht sofort senden möchten. So können wir es tun:

>>> request = requests.Request("GET", "https://httpbin.org/get")

Das erste Argument des Request-Konstruktors ist das Verb, das wir verwenden möchten, und das zweite, die Ziel-URL. Es können die gleichen Parameter verwendet werden, die wir verwenden, wenn wir eine Anfrage direkt senden: Header, Parameter, Daten, JSON und Dateien. Sobald wir eine Anfrage erstellt haben, müssen wir sie "vorbereiten", bevor wir sie senden können:

>>> session = requests.Session()
>>> request = requests.Request("GET", "https://httpbin.org/get")
>>> prepared_request = session.prepare_request(request)
>>> response = session.send(prepared_request)

Wir könnten eine Anforderung auch mit der prepare-Methode des Request-Objekts selbst vorbereiten, anstatt session.prepare_request aufzurufen, aber in diesem Fall würde die Anforderung die Vorteile verlieren, Teil der Sitzung zu sein.

Auslösen einer Ausnahme, wenn der Antwortstatuscode nicht 200 ist

Der Statuscode, der von einem Server zurückgegeben wird, wenn eine Anforderung erfolgreich ist, ist 200. Wenn ein Fehler auftritt, z. B. wenn eine Ressource nicht gefunden wird oder wenn wir nicht berechtigt sind, auf sie zuzugreifen, werden andere Codes zurückgegeben (in diesem Fall 404 bzw. 403). Wenn dies geschieht und wir möchten, dass unser Code eine Ausnahme auslöst, müssen wir die raise_for_status Methode des requests.models.Response-Objekts aufrufen. Schauen wir uns an, wie sich der Code anders verhält, wenn wir ihn verwenden. Wir senden eine POST-Anfrage an einen Endpunkt, der nur das GET-Verb akzeptiert:

>>> response = requests.post('https://httpbin.org/get')
>>> response.status_code
405
>>> response.reason
'METHOD NOT ALLOWED'

Da wir das falsche HTTP-Verb verwendet haben, war der Antwortstatuscode wie erwartet 405, und der entsprechende "Grund" ist METHOD NOT ALLOWED, es wurde jedoch keine Ausnahme ausgelöst. Damit eine fehlerhafte Anforderung eine Ausnahme auslösen kann, müssen wir die raise_for_status Methode aufrufen, nachdem wir die Anforderung gesendet haben:

>>> response = requests.post('https://httpbin.org/get')
>>> response.raise_for_status()
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 405 Client Error: METHOD NOT ALLOWED for url: https://httpbin.org/get

Da wir raise_for_status aufgerufen haben, hat die Anforderung dieses Mal eine requests.exceptions.HTTPError-Ausnahme ausgelöst.

Schlüsse

In diesem Artikel, dem zweiten der Serie über das Ausführen von HTTP-Anfragen mit Python, haben wir uns
auf die Verwendung der externen Anforderungsbibliothek konzentriert, die es uns ermöglicht, sowohl einfache als auch komplexe Anfragen
in wenigen Codezeilen auszuführen. Möchten Sie mehr darüber erfahren? Die offizielle Dokumentation ist nur einen Klick entfernt!

Verwandte Artikel: