Daten aus Schnittstellen und Webseiten holen: JSON seitenweise abrufen, HTML auslesen, Fehler abfangen und fair bleiben.
Kernideen
Eine API liefert strukturierte Daten, meist JSON; sie ist immer der erste Weg, bevor man eine Webseite ausliest
Antworten kommen oft seitenweise; abgerufen wird, bis kein Verweis auf eine nächste Seite mehr kommt
HTML wird mit einem Parser gelesen und über CSS-Selektoren angesprochen, nicht mit regulären Ausdrücken
Was aus einer Webseite kommt, ist Text; Zahlen, Einheiten und Tausendertrennzeichen werden danach bereinigt
Fehler wie 429 (zu viele Anfragen) und 5xx werden mit Wartezeit und Wiederholung behandelt
Nutzungsbedingungen, robots.txt und eine vernünftige Abfragefrequenz gehören zur Arbeit dazu
Erklärung
Vorwissen:Datenimport und Formate für JSON und das Flachmachen verschachtelter Antworten, Textdaten und reguläre Ausdrücke für das Bereinigen danach. Diese Seite ruft keine echte Adresse ab: Der Build der Website soll nicht davon abhängen, ob ein fremder Server gerade antwortet. Die Antworten stehen deshalb als Text im Code, die Verarbeitung ist dieselbe wie bei einem echten Abruf.
API oder Scraping
API
Web-Scraping
Liefert
strukturierte Daten, meist JSON
HTML für Menschen gemacht
Stabilität
versioniert, dokumentiert
bricht bei jeder Umgestaltung der Seite
Regeln
Nutzungsbedingungen, oft Schlüssel und Kontingent
robots.txt, Nutzungsbedingungen, Urheberrecht
Wann
immer, wenn es eine gibt
nur wenn es keine API und keinen Datendownload gibt
Ein Abruf in beiden Sprachen
So sieht ein echter Abruf aus. Diese Listings laufen im Build nicht:
Eine Schnittstelle liefert Messungen in Seiten zu höchstens zwei Einträgen. Jede Antwort enthält die Daten und im Feld weiter den Schlüssel für die nächste Seite, auf der letzten Seite null. Alle Messungen sollen in eine Tabelle.
aus zwei Seiten zusammengesetzt; wer nur die erste Seite liest, bekommt 12.5
Mittel Chur
9.65
ebenso
Interpretation
Der häufigste Fehler bei Schnittstellen ist nicht ein falscher Abruf, sondern ein unvollständiger: Nur die erste Seite wird gelesen, und die Auswertung rechnet still mit einem Bruchteil der Daten. Die Zahl der Zeilen gehört deshalb gegen die Gesamtzahl geprüft, die viele Schnittstellen in der Antwort mitliefern.
Frage und Datenlage
Eine Webseite zeigt eine Preisliste als HTML-Tabelle und darunter eine Liste mit Hinweisen. Gesucht sind die Preise als Zahlen, der Lagerwert und der Hinweis, der als neu markiert ist.
print([li.get_text() for li in suppe.select("ul.hinweise li.neu")])
['Winkel neu im Sortiment']
Output Zeile für Zeile
Ausgabe
Wert hier
Wie er zu lesen ist
preis_chf
0.35, 0.20, 2.80
Einheit CHF und Leerzeichen entfernt, danach Zahl
lager_n
1200, 950, fehlend
das Hochkomma als Tausendertrennzeichen entfernt; k.A. wird zu einem fehlenden Wert
Lagerwert
610
Summe über die zwei Artikel mit Bestand; der Winkel fällt mangels Lagerzahl heraus
Hinweis
Winkel neu im Sortiment
über den Selektor ul.hinweise li.neu gefunden
Interpretation
Der CSS-Selektor beschreibt die Stelle im Dokument so, wie ein Stylesheet sie beschreibt: #preise ist das Element mit dieser ID, li.neu ein Listenpunkt mit der Klasseneu. Die Selektoren findet man, indem man in den Entwicklerwerkzeugen des Browsers auf das Element klickt. Ohne Bereinigung wäre die Lagerspalte Text, und 1'200 ergäbe keine Zahl. Dass der Lagerwert ohne den Winkel berechnet ist, gehört ausdrücklich dazugesagt.
Frage und Datenlage
Drei Abrufe gegen einen überlasteten Server. Er antwortet der Reihe nach mit den Codes 200, 429, 429, 200, 500, 503, 200. Jeder Auftrag wird höchstens dreimal versucht, die Wartezeit verdoppelt sich ab einer Sekunde. Gezeigt wird das Protokoll; gewartet wird hier nicht wirklich.
zweimal zu viele Anfragen, erst 1, dann 2 Sekunden gewartet
3
500, 503, dann 200
3 s
Serverfehler, gleiche Behandlung
Interpretation
Die wachsende Wartezeit entlastet den Server genau dann, wenn er überlastet ist. Sofortige Wiederholungen würden die Überlastung verstärken. Nach dem letzten Versuch wird nicht still weitergemacht, sondern der Auftrag als fehlgeschlagen protokolliert; sonst fehlt am Ende ein Teil der Daten, ohne dass es jemand merkt. In der Praxis übernehmen httr2::req_retry() und der Retry-Mechanismus von requests diese Logik.