mitmario.dev

requests

Python Sandbox 4 Min Lesezeit 4 BeispieleLektion 2 von 5

Für eine Liste von Städten müsste man die Adresse jedes Mal zusammenkleben, erst /wetter?stadt= und dann den Namen. Das geht eine ganze Weile gut. Und dann geht es auf eine Art schief, die man nicht sofort merkt. requests kann das besser, beim Bauen der Adresse und beim Lesen der Antwort.

Parameter als Dictionary

Die Stadt als Parameter
import requests

import wetterdienst

wetterdienst.starten()
ADRESSE = "http://127.0.0.1:8000"

antwort = requests.get(ADRESSE + "/wetter", params={"stadt": "Frankfurt am Main"}, timeout=2)
print(antwort.url)
print(antwort.status_code)
print(antwort)

requests.get nimmt die Parameter als Dictionary hinter params=. Der Name des Parameters ist der Schlüssel, sein Wert ist der Wert. Die Adresse selbst hört dann beim Pfad auf, und requests hängt das Fragezeichen und alles Weitere an.

Was dabei herauskommt, steht in antwort.url und damit in der ersten Zeile der Ausgabe: stadt=Frankfurt+am+Main. Jedes Leerzeichen ist zu einem + geworden. In einer Adresse dürfen nämlich keine Leerzeichen stehen, und ein Umlaut wie in „München“ wird genauso umgeschrieben, zu M%C3%BCnchen. Beim Dienst wird daraus wieder der Name.

Bei mehreren Parametern musst du dich außerdem nicht um ? und & kümmern. Aus params={"stadt": "Erfurt", "tage": 3} wird ?stadt=Erfurt&tage=3.

In der letzten Zeile steht, was print(antwort) ausgibt, nämlich <Response [200]>. Das ist die Antwort als Ganzes, ein Objekt mit Status, Kopfzeilen und Rumpf, und in den eckigen Klammern steht sein Status. Die Daten selbst stehen da nicht. Die muss man erst herausholen.

Von Hand geklebt
import requests

import wetterdienst

wetterdienst.starten()
ADRESSE = "http://127.0.0.1:8000"

stadt = "Halle & Umgebung"
von_hand = requests.get(ADRESSE + "/wetter?stadt=" + stadt, timeout=2)
print(von_hand.url)
print(von_hand.text)
mit_params = requests.get(ADRESSE + "/wetter", params={"stadt": stadt}, timeout=2)
print(mit_params.url)
print(mit_params.text)

Wenn man die Adresse von Hand zusammenklebt, mit ADRESSE + "/wetter?stadt=" + stadt, klappt das mit Leerzeichen und Umlauten erstaunlich lange. requests bessert so eine Adresse nämlich noch aus. Mit urllib aus der letzten Lektion wäre beim Leerzeichen schon Schluss, mit URL can't contain control characters, und beim Umlaut mit einem UnicodeEncodeError.

Schief geht es bei einem Zeichen, das in einer Adresse eine Aufgabe hat. Ein & trennt zwei Parameter. Von „Halle & Umgebung“ kommt beim Dienst deshalb nur „Halle “ an, samt Leerzeichen und ohne jede Warnung. Mit params wird das & als %26 geschrieben und bleibt Teil des Namens.

Beide Male lautet die Antwort „Unbekannte Stadt“. Das liegt nur daran, dass der Dienst keinen Ort mit diesem Namen kennt. Richtig gestellt ist die Frage erst beim zweiten Mal.

Wie lange das Programm wartet

Ohne timeout wartet requests auf eine Antwort, so lange es eben dauert. Wenn der Dienst am anderen Ende hängt, wartet dein Programm mit, Minuten lang oder für immer, und du siehst nicht, warum es nicht weitergeht. Mit timeout=2 gibt es nach zwei Sekunden ohne Antwort auf und meldet einen Fehler.

timeout gehört deshalb an jede Anfrage. Auch wenn der Dienst bisher immer schnell war, denn gerade dann vergisst man es.

Wie viele Sekunden es sein sollen, hängt vom Dienst ab. Bei einem Dienst im Internet sind fünf oder zehn üblich. Der hier läuft auf derselben Maschine und antwortet in Millisekunden, da sind zwei reichlich. Was passiert, wenn die Zeit abläuft, und wie das Programm dann weitermacht, kommt in der nächsten Lektion.

Erst ansehen, dann hineingreifen

Erst ansehen, dann hineingreifen
import json

import requests

import wetterdienst

wetterdienst.starten()
ADRESSE = "http://127.0.0.1:8000"

antwort = requests.get(ADRESSE + "/wetter", params={"stadt": "Stuttgart"}, timeout=2)
daten = antwort.json()
print(json.dumps(daten, indent=2, ensure_ascii=False))
print(daten["aktuell"]["wind"], "km/h Wind")

antwort.json() gibt ein Dictionary zurück, und das ist oft verschachtelt. In diesem hier stehen die Stadt, die Uhrzeit der Messung und unter "aktuell" ein zweites Dictionary mit Temperatur, Wetter und Wind.

Wie eine Antwort aufgebaut ist, steht in der Beschreibung des Dienstes. Schneller ist es, sie sich einmal anzusehen. Dafür gibt es json.dumps mit indent=2 aus Lektion 12.5, das die Antwort eingerückt hinschreibt, jede Ebene zwei Leerzeichen weiter rechts. Mit ensure_ascii=False bleibt das ö ein ö. So siehst du auf einen Blick, wo was liegt.

Danach greift man Ebene für Ebene hinein, mit einer eckigen Klammer je Ebene. daten["aktuell"] ist das innere Dictionary und daten["aktuell"]["wind"] der Wind darin, 14 km/h in Stuttgart. Wenn du ein Feld öfter brauchst, bekommt die innere Ebene einen eigenen Namen, aktuell = daten["aktuell"], und danach reicht aktuell["wind"].

Felder, die man nicht braucht, lässt man einfach liegen, so wie hier die Uhrzeit. Eine Schnittstelle schickt fast immer mehr mit, als ein einzelnes Programm verwendet.

Die Antwort ist noch kein Dictionary

Die Antwort ist noch kein Dictionary
import requests

import wetterdienst

wetterdienst.starten()
ADRESSE = "http://127.0.0.1:8000"

antwort = requests.get(ADRESSE + "/wetter", params={"stadt": "Hannover"}, timeout=2)
print(antwort["aktuell"]["temperatur"])

Hier steht antwort["aktuell"], und das endet mit TypeError: 'Response' object is not subscriptable. Übersetzt heißt das: In ein Objekt vom Typ Response kann man nicht mit eckigen Klammern hineingreifen. antwort ist die ganze Antwort aus dem ersten Beispiel. Das Dictionary gibt es erst mit antwort.json().

Fast genauso sieht es aus, wenn die Klammern hinter json fehlen. antwort.json["aktuell"] endet mit TypeError: 'method' object is not subscriptable, weil json ohne Klammern die Methode selbst ist und nicht ihr Ergebnis.

Steht in einer Meldung not subscriptable, lohnt sich also immer die Frage, ob vor der eckigen Klammer wirklich das steht, was du meinst.

Was die Aufgabe will

Diesmal geht es nicht um eine Stadt, sondern um eine ganze Liste, und jede Stadt bekommt ihre eigene Anfrage. Für den Moment antwortet der Dienst bei jeder Stadt auf der Liste freundlich. Was passiert, wenn nicht, ist die nächste Lektion.

Zum Mitnehmen

Mit requests.get(adresse, params={...}, timeout=2) landen die Parameter aus dem Dictionary richtig in der Adresse, auch Leerzeichen, Umlaute und ein &. Fehlt timeout, kann das Programm ewig hängen bleiben. Den Status liest man aus antwort.status_code, und erst antwort.json() liefert das Dictionary, in das man dann Ebene für Ebene hineingreift.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.