Lerne Coding
Beautiful Soup 4 - Web Scraping mit Python
05.10.2020

Beautiful Soup 4 (Schöne Suppe) 🍲

Inhaltsverzeichnis
[[TABLE OF CONTENTS]]
access_timeGeschätzte Lesezeit ca. Minuten

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.

Installation von Beautiful Soup 4

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

Methoden in Beautiful Soup 4 anhand eines HelloCoding Artikels

Als Beispiel eines HTML Dokuments nehmen wir den XML Artikel von HelloCoding und extrahieren daraus die folgenden Daten:

  • Titel
  • Titelbild
  • Beschreibung
  • Veröffentlichungsdatum
  • Datum der letzten Änderung
  • Kategorie
  • Autor
  • Überschriften
  • Alle Links

Natürlich kann man noch deutlich mehr Daten extrahieren, aber für ein einfaches Verständnis von BS4 reicht diese Auswahl.

Imports die wir benötigen um mit dem Scraping zu Starten

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

Daten aus der Webseite Laden

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

Daten auswerten

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')

Titel auslesen

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

Metadaten auslesen

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.

CSS Selektoren verwenden

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

Daten als JSON speichern

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)

Zusammenfassung

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",
            ......
    }
}

Achtsam handeln

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.

Fazit

Mit Beautiful Soup 4 kannst du schnell Daten aus einer Webseite extrahieren anhand von CSS Selektoren und verschiedener Methoden.

Kommentare zum Artikel
Pascal Klein schreibt ... Kommentar vom 07.04.2022
Frage

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?

Antworten
Pascal Klein
Antwort von Felix Schürmeyer Kommentar vom 07.04.2022
Antwort - Anpassungen

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

Felix Schürmeyer
Kommentar schreiben

Vom Autor Empfohlen
close