Web Scraping und Parsen von HTML in Python mit Beautiful Soup

October 22, 2019
Autor:in:
Sam Agnew
Twilion

Web Scraping und Parsen von HTML in Python mit Beautiful Soup

Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von Web Scraping and Parsing HTML in Python with Beautiful Soup. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an help@twilio.com freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)

Im Internet findet sich eine unglaublich große Vielfalt an Daten, die wir nach Belieben verwenden können. Der programmgesteuerte Zugriff auf diese Daten ist allerdings oft schwierig, sofern er nicht über eine dedizierte REST API bereitgestellt wird. Aber mit einem Python-Tool wie Beautiful Soup können wir diese Daten direkt aus den Webseiten scrapen und parsen, um sie für unsere Projekte und Anwendungen zu nutzen.

Ein Beispiel dafür wäre das Scraping von MIDI-Daten aus dem Internet, um ein neuronales Netzwerk mithilfe von Magenta so zu trainieren, dass es Musik im klassischen Nintendo-Stil generiert. Dazu brauchen wir zunächst MIDI-Dateien mit Musik aus alten Nintendo-Spielen. Mit Beautiful Soup können wir uns diese Daten aus dem Video Game Music Archive holen.

Vorbereitende Aufgaben und Einrichten von Abhängigkeiten

Als Erstes müssen wir eine aktuelle Version von Python 3 und pip installieren. Danach erstellen und aktivieren wir eine virtuelle Umgebung, und erst dann geht es ans Installieren der Abhängigkeiten.

Wir müssen die Requests-Bibliothek installieren, damit wir HTTP-Anfragen zum Abrufen von Daten von der Webseite durchführen können, und brauchen Beautiful Soup zum Parsen des HTML.

Bei aktivierter virtueller Umgebung führen wir nun den folgenden Befehl auf dem Terminal aus:

pip install requests==2.22.0 beautifulsoup4==4.8.1

Wir verwenden Beautiful Soup 4, weil es die neueste Version ist und Beautiful Soup 3 nicht mehr weiterentwickelt bzw. unterstützt wird.

Requests verwenden, um Daten zum Parsen mit Beautiful Soup zu extrahieren

Als Erstes schreiben wir Code, um das HTML der Webseite zu erfassen und zu prüfen, wie wir das Parsing starten. Mit dem folgenden Code senden wir eine GET-Anfrage an die gewünschte Webseite und erstellen ein BeautifulSoup-Objekt mit dem HTML der Seite:

import requests
from bs4 import BeautifulSoup


vgm_url = 'https://www.vgmusic.com/music/console/nintendo/nes/'
html_text = requests.get(vgm_url).text
soup = BeautifulSoup(html_text, 'html.parser')

Mit diesem soup-Objekt können wir durch das HTML navigieren und die gewünschten Daten suchen. Wenn wir beispielsweise im Anschluss an die vorige Anfrage in einer Python-Shell den Befehl soup.title ausführen, erhalten wir den Titel der Webseite. Der Befehl print(soup.get_text()) gibt den gesamten Text auf der Seite zurück.

Mit Beautiful Soup vertraut werden

Die Methoden find() und find_all() gehören zu den leistungsstärksten Tools in unserem Werkzeugkasten. soup.find() ist eine gute Wahl, um nach einem bestimmten Element zu suchen, z. B. dem Tag body. Auf dieser Seite erhalten wir mit soup.find(id='banner_ad').text den Text aus dem HTML-Element für die Banner-Werbung.

soup.find_all() ist die gängigste Methode, auf die wir bei unseren Web Scraping-Abenteuern zurückgreifen werden. Wir können damit durch alle Hyperlinks auf der Seite iterieren und die zugehörigen URLs ausgeben:

for link in soup.find_all('a'):
    print(link.get('href'))

Wir können find_all auch mit verschiedenen Argumenten kombinieren, z. B. mit regulären Ausdrücken oder Tag-Attributen, um die Suche gezielt und nach Bedarf zu filtern. Viele weitere coole Funktionen sind in der Dokumentation zu finden.

Mit BeautifulSoup HTML parsen und navigieren

Bevor wir weiteren Code zum Parsen des gewünschten Content schreiben, sehen wir uns das vom Browser gerenderte HTML an. Jede Webseite ist anders – um die richtigen Daten zurückzuerhalten, ist daher ein gewisses Maß an Kreativität, Mustererkennung und Experimentierfreudigkeit gefragt.

MIDI-Dateien auf der Website

Unser Ziel besteht darin, eine Reihe von MIDI-Dateien herunterzuladen. Allerdings gibt es auf dieser Webseite zahlreiche doppelte Tracks sowie Remixes der Songs. Wir brauchen aber nur jeweils eine Version jedes Songs. Und da wir letztendlich mit den betreffenden Daten ein neuronales Netzwerk so trainieren wollen, dass es stilechte Nintendo-Musik generiert, ist es nicht sinnvoll, benutzerspezifische Remixes für das Trainieren zu verwenden.

Wer Code zum Parsen einer Webseite schreibt, sollte sich auch die Entwicklertools zunutze machen, die in den meisten modernen Browsern verfügbar sind. Wenn wir mit der rechten Maustaste auf das gewünschte Element klicken, können wir das HTML prüfen, das dem Element zugrunde liegt. So finden wir heraus, wie wir den programmgesteuerten Zugriff auf die gewünschten Daten hinbekommen.

Element überprüfen

Als Nächstes gehen wir mit der find_all-Methode durch alle Links auf der Seite. Dabei filtern wir anhand regulärer Ausdrücke nur die URLs heraus, die MIDI-Dateien mit Text ohne Klammern enthalten. Auf diese Weise schließen wir alle Duplikate und Remixes aus.

Wir erstellen eine Datei mit dem Namen nes_midi_scraper.py und geben den folgenden Code darin ein:

import re

import requests
from bs4 import BeautifulSoup


vgm_url = 'https://www.vgmusic.com/music/console/nintendo/nes/'
html_text = requests.get(vgm_url).text
soup = BeautifulSoup(html_text, 'html.parser')


if __name__ == '__main__':
    attrs = {
        'href': re.compile(r'\.mid$')
    }

    tracks = soup.find_all('a', attrs=attrs, string=re.compile(r'^((?!\().)*$'))

    count = 0
    for track in tracks:
        print(track)
        count += 1
    print(len(tracks))

Mit diesem Code filtern wir durch alle gewünschten MIDI-Dateien auf der Seite, drucken jeweils das entsprechende Link-Tag und dann die Anzahl der gefilterten Dateien.

Wir führen den Code im Terminal mit dem Befehl python nes_midi_scraper.py aus.

Herunterladen der gewünschten MIDI-Dateien von der Webseite

Wir haben also jetzt den nötigen Code, um durch jede der gewünschten MIDI-Dateien zu iterieren. Nun brauchen wir Code, um alle dieser Dateien herunterzuladen.

Wir fügen dem Code in nes_midi_scraper.py eine Funktion mit dem Namen download_track hinzu und rufen diese Funktion für jeden Track auf, während wir durch die Dateien iterieren:


import re

import requests
from bs4 import BeautifulSoup


vgm_url = 'https://www.vgmusic.com/music/console/nintendo/nes/'
html_text = requests.get(vgm_url).text
soup = BeautifulSoup(html_text, 'html.parser')


def download_track(count, track_element):
    # Get the title of the track from the HTML element
    track_title = track_element.text.strip().replace('/', '-')
    download_url = '{}{}'.format(vgm_url, track_element['href'])
    file_name = '{}_{}.mid'.format(count, track_title)

    # Download the track
    r = requests.get(download_url, allow_redirects=True)
    with open(file_name, 'wb') as f:
        f.write(r.content)

    # Print to the console to keep track of how the scraping is coming along.
    print('Downloaded: {}'.format(track_title, download_url))


if __name__ == '__main__':
    attrs = {
        'href': re.compile(r'\.mid$')
    }

    tracks = soup.find_all('a', attrs=attrs, string=re.compile(r'^((?!\().)*$'))

    count = 0
    for track in tracks:
        download_track(count, track)
        count += 1
    print(len(tracks))

Mit dieser download_track-Funktion geben wir das Beautiful Soup-Objekt, das für das HTML-Element der URL steht, an die MIDI-Datei weiter – zusammen mit einer eindeutigen Zahl im Dateinamen, um potenzielle Namenskonflikte zu vermeiden.

Wir führen diesen Code von dem Verzeichnis aus, in dem wir alle MIDI-Dateien speichern möchten. Auf dem Terminal-Bildschirm sollten wir sehen, dass alle 2230 MIDIs heruntergeladen wurden (Zahl zum Zeitpunkt der Erstellung dieses Blogs). Dieses konkrete praktische Beispiel verdeutlicht, was alles mit Beautiful Soup möglich ist.

Terminal-Ausgabe

Die endlose Weite des World Wide Web

Wir wissen nun, wie man programmgesteuert Inhalte aus Webseiten extrahieren kann, das heißt, wir haben jetzt Zugang zu einer gigantischen Datenquelle mit allem, was wir für unsere Projekte brauchen. Zu beachten ist, dass jegliche Änderungen am HTML einer Webseite zu Fehlern in unserem Code führen können. Daher ist es wichtig, dass wir alles auf dem neuesten Stand halten, wenn wir Anwendungen auf der Basis dieses Codes entwerfen.

Wie könnten wir die aus dem Video Game Music Archive extrahierten Daten nun weiterverwenden? Mit einer Python-Bibliothek wie Mido für die Arbeit mit MIDI-Daten können wir die Daten bereinigen, mit Magenta können wir ein neuronales Netzwerk damit trainieren oder eine Telefonnummer erstellen, die Interessierte anrufen können, um Nintendo-Musik zu hören.

Ich bin gespannt auf eure Ergebnisse. Ihr könnt mich gerne kontaktieren, um eure Erfahrungen zu teilen oder Fragen zu stellen.