Aussehen
Daten ohne API auslesen 🐍🐍🐍
Nicht jede Seite bietet eine API an. Manchmal stehen die Daten, die du brauchst, nur in der Webseite selbst — im Veranstaltungskalender eines Clubs, in einer Preisliste, in einer Tabelle mit Messwerten.
Dann bleibt der umgekehrte Weg: Du lädst die Seite so herunter, wie sie ein Browser bekommt, und suchst dir aus dem HTML heraus, was du brauchst. Das nennt man Web Scraping, und es ist deutlich mühsamer als eine API — weil du dich auf etwas verlässt, das nicht für dich gemacht wurde.
Erst überlegen, dann auslesen
Technisch kannst du jede Seite auslesen. Dürfen darfst du das nicht immer.
- Nutzungsbedingungen. Viele Seiten verbieten automatisches Auslesen ausdrücklich.
- robots.txt. Unter dieser Adresse legt fast jede Seite ab, was sie automatisierten Zugriffen erlaubt — de.wikipedia.org/robots.txt etwa. Das ist keine technische Sperre, sondern eine Bitte; halte dich trotzdem daran.
- Urheberrecht. Texte und Bilder gehören jemandem. Für dich auswerten ist etwas anderes als weiterveröffentlichen.
- Last. Lade eine Seite einmal und arbeite dann mit der heruntergeladenen Fassung weiter. Nicht in einer Schleife hundertmal.
Was du brauchst
pip install requests beautifulsoup4Requests holt die Seite, BeautifulSoup macht aus dem HTML etwas Durchsuchbares. Dazu Grundwissen in HTML — falls das eine Weile her ist, hilft HTML & CSS.
HTML ist ein Baum
Eine Webseite ist keine Textwüste, sondern eine Verschachtelung: Im body steckt ein div, darin eine h1, daneben ein p. Wie diese Schachtelung entsteht, steht bei den Verschachtelungen in HTML — und genau sie nutzt du jetzt zum Suchen.
Als Übungsobjekt nimmst du dieses Buch. Wir lesen die Startseite von buch.informatik.cc aus und holen uns die Kapitel heraus.
python
from bs4 import BeautifulSoup
import requests
seite = requests.get("https://buch.informatik.cc/")
suppe = BeautifulSoup(seite.content, "html.parser")
for ueberschrift in suppe.find_all("h1"):
print(ueberschrift.text)find_all("h1") liefert alle Hauptüberschriften der Seite als Liste. .text holt den sichtbaren Text ohne die Auszeichnungen drumherum. Heraus kommt genau eine Zeile, und die sieht seltsam aus: buch.informatik.ccInformatikschulbuch. Im HTML sind das zwei Teile nebeneinander, die der Browser untereinander setzt — .text klebt sie zusammen, weil ihm die Gestaltung nichts sagt.
Die Kapitel stehen woanders. Jede Kachel auf der Startseite trägt ihre Überschrift in einer h2 mit der Klasse title:
python
for ueberschrift in suppe.find_all("h2", class_ = "title"):
print(ueberschrift.text)Jetzt kommen zehn Zeilen heraus, von „HTML & CSS" bis „Fake erkennen". Der Unterstrich in class_ ist eine Notlösung: class ist in Python ein Schlüsselwort, deshalb hängt BeautifulSoup einen an.
Erst schauen
Öffne die Seite im Browser und drück F12. In den Entwicklerwerkzeugen siehst du, in welchen Elementen die Daten wirklich stecken — und ob es h1, h2 oder ein div mit einer bestimmten Klasse ist.
Lass dir danach mit find_all genau diese Elemente ausgeben.
Warum „Suppe"?
Die Bibliothek heißt BeautifulSoup, weil unsauberes HTML im Englischen tag soup genannt wird — ein Buchstabensalat aus Elementen, bei dem Anfang und Ende oft nicht zusammenpassen. Die Bibliothek macht daraus trotzdem einen brauchbaren Baum.
Wo du üben solltest
Dass wir hier ausgerechnet dieses Buch auslesen, hat einen Grund: Es gehört dem Autor, du darfst es also bedenkenlos abrufen, und eine so kleine Seite bringst du mit deinen Versuchen auch nicht in Verlegenheit. Such dir zum Üben immer ein Ziel, bei dem beides stimmt.
Reguläre Ausdrücke
Der Text in einem Element ist oft noch keine saubere Angabe. Ein Spiel bauen 🐍🐍 enthält einen Titel und eine Schwierigkeit in einem Stück. Zum Auseinandernehmen nimmt man ein Suchmuster — dafür ist das eingebaute Modul re da.
python
import re
zeile = "Ein Spiel bauen 🐍🐍"
treffer = re.search(r"^(.*) (🐍+)$", zeile)
titel = treffer.group(1)
schwierigkeit = len(treffer.group(2))^ ist der Anfang der Zeichenkette, $ das Ende, (.*) heißt „alles Übrige", 🐍+ „mindestens eine Schlange". Das r vor dem Anführungszeichen macht daraus eine Rohzeichenkette: Python lässt die Rückstriche dann in Ruhe, statt sie selbst deuten zu wollen. Bei Suchmustern schreibt man es immer davor.
Die runden Klammern bilden Gruppen — jede davon kannst du hinterher einzeln herausholen, in der Reihenfolge ihres Auftretens. Hier steht in Gruppe 1 „Ein Spiel bauen" und in Gruppe 2 „🐍🐍", und weil die Schlangen gezählt werden sollen, reicht len.
Die wichtigsten Bausteine
Zeichenklassen — was für ein Zeichen an dieser Stelle stehen darf:
| Muster | Bedeutung | Beispiel |
|---|---|---|
. | ein beliebiges Zeichen | H.ll trifft Hall und Hell |
[abc] | eines der aufgezählten Zeichen | [bhm]ut trifft but, hut, mut |
[a-z] | ein Zeichen aus dem Bereich | [A-Z] trifft jeden Großbuchstaben |
[^abc] | alles außer den aufgezählten | [^0-9] trifft alles, was keine Ziffer ist |
\d | eine Ziffer, kurz für [0-9] | \d\d trifft 26 |
\w | ein Buchstabe, eine Ziffer oder _ | \w+ trifft ein ganzes Wort |
\s | ein Leerzeichen, Tabulator, Zeilenumbruch | \s trennt zwei Wörter |
\. | ein echter Punkt | \d\.\d trifft 3.1 |
Wiederholungen — stehen hinter dem, was sich wiederholen soll:
| Muster | Bedeutung | Beispiel |
|---|---|---|
* | beliebig oft, auch gar nicht | ab* trifft a, ab, abbb |
+ | mindestens einmal | 🐍+ trifft 🐍, 🐍🐍🐍 |
? | einmal oder gar nicht | colou?r trifft beide Schreibweisen |
{3} | genau dreimal | \d{4} trifft eine Jahreszahl |
{2,4} | zwei- bis viermal | \d{2,4} trifft 26 und 2026 |
Anker — legen fest, wo im Text gesucht wird:
| Muster | Bedeutung | Beispiel |
|---|---|---|
^ | Anfang der Zeichenkette | ^/ trifft nur Adressen, die mit / beginnen |
$ | Ende der Zeichenkette | \.jpg$ trifft nur, was auf .jpg endet |
\b | Wortgrenze | \bab\b trifft ab, nicht abends |
Gruppen — das, was du hinterher herausholen willst:
| Muster | Bedeutung | Beispiel |
|---|---|---|
(...) | Gruppe, später mit .group(1) lesbar | (\d+)-(\d+) zerlegt 12-34 |
(a|b) | Alternative, entweder oder | (Mo|Di) trifft Mo und Di |
(?:...) | gruppiert, ohne mitgezählt zu werden | (?:ha)+ trifft hahaha |
Zum Ausprobieren gibt es regexr.com: links das Muster, rechts der Text, und Treffer werden sofort farbig markiert. Mehr als die Tabellen oben brauchst du fürs Erste nicht.
Eines noch, und es ist wichtiger als alle Tabellen: Ein Suchmuster gehört auf den Text in einem Element, nie auf das HTML drumherum. Für die Verschachtelung hast du den Baum, und der ist dafür gebaut. Wer HTML mit Suchmustern auseinandernimmt, kämpft gegen jede geschlossene Klammer einzeln — und verliert.
Das vollständige Beispiel
Jetzt beides zusammen. Zu jedem Kapitel wollen wir drei Angaben: den Kurznamen aus der Adresse, die Überschrift und die Beschreibung darunter.
python
from bs4 import BeautifulSoup
import requests
seite = requests.get("https://buch.informatik.cc/")
suppe = BeautifulSoup(seite.content, "html.parser")
for ueberschrift in suppe.find_all("h2", class_ = "title"):
kasten = ueberschrift.find_parent("a")
if kasten == None:
continue
kurzname = kasten.get("href").strip("/")
beschreibung = ueberschrift.next_sibling
print(kurzname, "-", ueberschrift.text)
print(beschreibung.text)Heraus kommen neun Kapitel, das erste davon:
html-css - HTML & CSS
Lerne HTML und CSS im Selbstudium.Drei Stellen verdienen Aufmerksamkeit.
find_parent("a"). Die Überschrift steht im Baum innerhalb des Links, der auf das Kapitel zeigt. Um an die Adresse zu kommen, musst du also nach oben. Im Baum bewegst du dich in alle Richtungen: nach unten mit find, zur Seite mit next_sibling, nach oben mit find_parent.
if kasten == None: continue. Zehn Überschriften hat die Seite, neun Kapitel kommen heraus — die Calliope-Kachel ist anders gebaut und steckt in keinem Link, weil sie gleich zwei davon enthält. Genau das ist beim Scraping der Normalfall, nicht die Ausnahme: Irgendein Element passt immer nicht ins Schema. Ohne diese Zeile bricht das Programm bei ihm ab.
next_sibling. Die Beschreibung steht nicht in der Überschrift, sondern als nächstes Element daneben.
Analysieren und umbauen
- Führ das Programm aus und schau dir an, was herauskommt.
- Erkläre für jede Zeile, was sie tut.
- Bau aus dem Kurznamen die vollständige Adresse des Kapitels und gib sie mit aus.
- Schreib die Ergebnisse in eine Datei statt auf den Bildschirm.
- Ruf zusätzlich jede Kapitelseite auf und hol dir von dort die Überschriften der Unterkapitel. Denk an die Last: Das sind neun Abrufe, nicht neunhundert.
Warum das morgen kaputt sein kann
Eine API ist ein Versprechen: Der Betreiber sagt zu, dass die Daten so kommen, wie sie dokumentiert sind. Beim Scraping gibt es kein solches Versprechen. Ändert jemand das Aussehen der Seite — aus h2 wird h3, die Klasse heißt plötzlich anders —, findet dein Programm nichts mehr.
Die Klasse title im Beispiel oben stammt nicht einmal vom Autor dieses Buchs, sondern aus dem Programm, mit dem die Seite gebaut wird. Beim nächsten Update kann sie anders heißen, ohne dass sich am Buch etwas geändert hätte.
Das ist kein Fehler in deinem Programm. Es ist der Preis dieses Wegs, und du solltest ihn kennen, bevor du etwas darauf aufbaust.
Weitere Ideen
| Projekt | |
|---|---|
| Dieses Buch offline: alle Kapitelseiten einsammeln und in eine Textdatei schreiben | 🐍🐍 |
| Aus dem Wiki deines Lieblingsspiels alle Gegenstände mit ihren Werten holen und die beste Ausrüstung ausrechnen | 🐍🐍🐍 |
| Die Termine deines Vereins einsammeln und als Kalenderdatei aufs Handy legen | 🐍🐍🐍 |
Die vollständige Anleitung zu BeautifulSoup:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/