Beautiful Soup 4 ist ein Paket für Python, das zum Selektieren von Elementen in HTML und XML verwendet wird. So könnte man zum Beispiel den Inhalt einer Webseite auslesen und in eine JSON Datei exportieren, um diese weiterzuverarbeiten. Oder aber auch direkt eine XML String zum Beispiel von einer Rest-API kann geparst werden. Dieser Prozess zum Lesen von Daten aus einer Webseite wird auch als Web Scraping bezeichnet, in Python kann dieses mit Beautiful Soup 4 realisiert werden.
Der Vorteil von Beautiful Soup 4 (kurz "bs4") gegenüber zum Beispiel Regex ist, dass die Selektierung deutlich einfacher ist. Die Verwendung des Paketes ist außerdem sehr präzise, da CSS Selektoren verwendet werden können.
Beautiful Soup 4 parst das übergebene Dokument als Baum mit Attributen, Tags, NavigableString und Comment. Über diese Typen kann eine Selektierung im Baum vorgenommen werden, um entweder Werte im Dokument zu ändern oder zu extrahieren.
Die Installation von BS4 ist ein Kinderspiel. Es gibt die Möglichkeit das Paket über den Python Package Index (PyPI) zu Installieren, der gängige PIP Paketmanager.
Folgenden Befehl musst du dafür im Terminal eingeben:
pip install beautifulsoup4
Alternativ kannst du unter Linux auch den internen Paketmanger "apt" verwenden.
apt-get install python3-bs4
Als Beispiel eines HTML Dokuments nehmen wir den XML Artikel von HelloCoding und extrahieren daraus die folgenden Daten:
Natürlich kann man noch deutlich mehr Daten extrahieren, aber für ein einfaches Verständnis von BS4 reicht diese Auswahl.
Als erstes importieren wir von "bs4" das Modul "BeautifulSoup4". Als zweites importieren wir das Standard "request" Modul. Dieses ist relevant, um die Daten von HelloCoding.de abzufragen.
from bs4 import BeautifulSoup
import requests
Als erstes laden wir den gewünschten Artikel von der HelloCoding.de Webseite und prüfen, ob der Statuscode 200 entspricht. Dieser ist der Standard Statuscode, der bei einem erfolgreichen Abruf zurückgegeben wird. Tritt dieser Fall ein, nehmen wir das HTML Dokument aus der Antwort des Servers heraus.
page = requests.get("https://hellocoding.de/blog/glossar/x/xml")
if page.status_code == 200:
content = page.content
Wir erstellen uns ein leeres Dictionary, um die Daten später speichern zu können. Danach erstellen wir ein Soup Objekt aus dem HTML Dokument, das wir soeben von HelloCoding.de abgefragt haben. Falls du ein XML Document Parsen willst solltest du den Parser auf lxml-xml
oder xml
einstellen.
Data = {}
DOMdocument = BeautifulSoup(content, 'html.parser')
Der Titel steht immer in einem <title>
-Tag. Um nur den Inhalt dieses Tags zu bekommen und nicht den Tag selbst, ergänzen wir ein .string
. Das vereinfacht uns später das Lesen der Daten.
Data['title'] = DOMdocument.title.string
Mit .find
können wir die Metadaten der Webseite selektieren um Bilder, die Beschreibung, Änderungszeiten und das Veröffentlichungsdatum auszulesen.
Data['Image'] = DOMdocument.find("meta", property="og:image")['content']
Data['Description'] = DOMdocument.find("meta", property="og:description")['content']
Data['Published Time'] = DOMdocument.find("meta", property="article:published_time")['content']
Data['Update Time'] = DOMdocument.find("meta", property="og:updated_time")['content']
property
gibt in dem Fall an, welche Metadaten wir benötigen und mit content
erhalten wir das content
Attribut des jeweiligen Tags.
In BS4 können wir ebenfalls CSS Selektoren nutzen, um Elemente auszuwählen. Im folgenden Beispiel nutzen wir die select_one
Methode, um nur ein Element zu erhalten, woraus wir den Namen des Autors extrahieren.
Data['Autor'] = DOMdocument.select_one('aside .autor-box span.h6').text.replace('Autor: ','')
Es ist ebenfalls möglich über diese Selektoren ein bestimmtes Element zu selektieren und davon Kinds-Elemente auszuwählen. Das benötigen wir, um die Kategorien des Artikels zu erhalten. Von diesem Element header .group .category .categoryItem
holen wir uns alle a
-Tags und ordnen dem Titel Attribut den href in einem Dictionary zu.
Data['Categorys'] = {}
for a in DOMdocument.select_one("header .group .category .categoryItem").find_all('a'):
Data['Categorys'][a.get('title')] = a.get('href')
Natürlich können wir über select
auch mehrere ähnliche Elemente zugleich selektieren. Diesen Fall haben wir zum Beispiel bei Überschriften. In dem nächsten Beispiel finden wir alle Überschriften, die im Inhalt der Seite liegen.
Data['Headlines'] = list()
for headline in DOMdocument.select('#contentTOC h1,#contentTOC h2,#contentTOC h3,#contentTOC h4,#contentTOC h5,#contentTOC h6'):
Data["Headlines"].append((headline.name,headline.string))
Diese fügen wir als Tupel einer Liste hinzu, um diese nach der Hierachie des Dokuments geordnet zu speichern.
Als letztes extrahieren wir, ähnlich wie die Kategorien, alle Links aus der Seite und speichern diese.
Data['Links'] = {}
for a in DOMdocument.find_all('a'):
Data['Links'][a.get('href')] = a.string
Um die Daten nun noch zu speichern, ergänzen wir am Anfang der Datei einen Import.
import json
Am Ende, innerhalb der if Abfrage vom StatusCode, ergänzen wir folgenden Befehl. Anhand des Parameters indent
definieren wir einen Einzug, welcher für eine schönere Ausgabe des JSON sorgt. Für weitere Infos zum Thema JSON in Python, kannst du in den Artikel "JSON in Python korrekt verwenden!" schauen.
json.dump(Data,open('article-save.json','w'),indent=4)
Hier das komplette Script, das du natürlich direkt mit Python3 testen kannst.
Natürlich gibt dir dieses Beispiel nur einen kleinen Einblick in BS4. Mit genug Zeit und Energie kannst du deutlich mehr Informationen aus einer Webseite extrahieren. Versuch doch mal weitere Elemente aus dem HelloCoding Artikel auszulesen. Sobald du das ganze auf einen anderen Artikel von HelloCoding anwendest, wirst du sehen, dass du die gleichen Eigenschaften eines anderen Artikels erhalten kannst.
import requests
from bs4 import BeautifulSoup
import json
page = requests.get("https://hellocoding.de/blog/glossar/x/xml")
if page.status_code == 200:
content = page.content
Data = {}
DOMdocument = BeautifulSoup(content, 'html.parser')
Data['Title'] = DOMdocument.title.string
Data['Image'] = DOMdocument.find("meta", property="og:image")['content']
Data['Description'] = DOMdocument.find("meta", property="og:description")['content']
Data['Published Time'] = DOMdocument.find("meta", property="article:published_time")['content']
Data['Update Time'] = DOMdocument.find("meta", property="og:updated_time")['content']
Data['Autor'] = DOMdocument.select_one('aside .autor-box span.h6').string.replace('Autor: ','')
Data['Categorys'] = {}
for a in DOMdocument.select_one("header .group .category .categoryItem").find_all('a'):
Data['Categorys'][a.get('title')] = a.get('href')
Data['Headlines'] = list()
for headline in DOMdocument.select('#contentTOC h1,#contentTOC h2,#contentTOC h3,#contentTOC h4,#contentTOC h5,#contentTOC h6'):
Data["Headlines"].append((headline.name,headline.string))
Data['Links'] = {}
for a in DOMdocument.find_all('a'):
Data['Links'][a.get('href')] = a.string
json.dump(Data,open('article-save.json','w'),indent=4)
Eine fertige JSON Datei könnte folgendermaßen aussehen (in diesem Beispiel gekürzt).
{
"Title": "Was ist XML und Wof\u00fcr brauch ich XML? | HelloCoding",
"Image": "https://hellocoding.de/images/category/x/xml/thumb/dark-xml-was-ist-das.jpg",
"Description": "Eine XML-Datei ist ein textbasiertes Datenformat, das dazu dient, Daten zu strukturieren. ",
"Published Time": "04.01.2020",
"Update Time": "23.06.2020",
"Autor": "Felix Sch\u00fcrmeyer",
"Categorys": {
"Artikel": "https://hellocoding.de/blog/",
"Entwickler Glossar": "https://hellocoding.de/blog/glossar/",
"X": "https://hellocoding.de/blog/glossar/x/"
},
"Headlines": [
[
"h1",
"Was sind XML-Dateien?"
],
......
],
"Links": {
"https://hellocoding.de": "Home",
"https://hellocoding.de/blog/coding-language/": "Coding ",
"https://hellocoding.de/blog/coding-language/allgemein/": "Allgemein",
......
}
}
Bitte beachte, dass du nicht mehrere 1.000 Seiten hintereinander crawlen, sondern eine Drosselung von 1-2 Sekunden verwenden solltest mittels eines Sleep Befehles. Je nach Server kannst du diesen überlasten. Grundsätzlich solltest du auch prüfen, ob die Webseite Guidelines besitzt, die das Crawling verbieten - und diese Entsprechende Respektieren.
Mit Beautiful Soup 4 kannst du schnell Daten aus einer Webseite extrahieren anhand von CSS Selektoren und verschiedener Methoden.
Hinterlasse mir gerne einen Kommentar zum Artikel und wie er dir weitergeholfen hat beziehungsweise, was dir helfen würde das Thema besser zu verstehen. Oder hast du einen Fehler entdeckt, den ich korrigieren sollte? Schreibe mir auch dazu gerne ein Feedback!
Guten Tag,
zu deinem Ergebnisse, warum beim Autor Feld das Programm abstürzt, das liegt daran, dass sich der CSS Selektor verändert hat durch Updates der Webseite. Habe diesen nun im Artikel korrigiert, danke!
Zu deiner Frage wie du am besten an den gesamten Textlichen Inhalts eines Bereiches oder eines Elementes kommst, in Python Beautiful Soup 4 kannst du das Attribute text
verwenden, das Beinhaltet den gesamten Textlichen Inhalt und entfernt HTML Elemente aus dem Resultat in Python.
Zum Beispiel, auf den Code & Variablen von oben bezogen würde zum Beispiel Folgendes funktionieren für den gesamten Body.
DOMdocument.body.text
Gruß, Felix
Netter Artikel. Ich habe mir das Beispiel unter Linux angeschaut, bis auf die Zeile mit Autor funktioniert der Code.
Frage hierzu: Ich möchte von einer Webseite Daten zeilenweise auslesen. Dazu finde ich bei BeautifulSoap keine Möglichkeit. Wie komme ich innerhalb des Codes an den eigentlichen Inhalt der Seite, also den Text, Content?