Website Scraper mit JSoup
Tags: CSS-Selektoren | HTML lernen | HTML-Tag | Java | Programmieren lernen | Rekursion | ScraperWorum geht´s?
In diesem Tutorial werden wir einen kleinen Website-Scraper mit Java programmieren. Ein Scraper hilft dir, Daten und Inhalte von Webseiten automatisiert zusammenzutragen. Anstatt also zum Beispiel selbst willhaben.at nach Jobs in der Stadt Kirchdorf zu durchsuchen, kannst du einen Scraper benutzen, der die willhaben.at-Website automatisch für dich „durchklickt“ und alle benötigten Informationen zusammenträgt. Du kannst dir dann aussuchen, was du mit den gesammelten Informationen machen möchtest. In diesem Fall werden wir sie im .csv-Format speichern, damit wir sie mit Excel weiter bearbeiten können.
Um dir den Anfang zu erleichtern, habe ich ein funktionierendes Java-Projekt auf Github gestellt, mit dem du anfangen kannst. Hole es dir in IntelliJ, indem du unter File/New/Project from Version Control… beim Feld URL folgendes eingibst:
https://github.com/bunterrichten/Scraper
Dann sollte das Projekt automatisch in IntelliJ auftauchen.
Starten kannst du es, indem du im src-Ordner einen Doppelklick auf die Scraper-Klasse machst udn dann auf einen der kleinen grünen Pfeile klickst, und dann auf „Run Scraper.main()“.
Das Programm sollte dann unten in der Konsole alle möglichen Jobs anzeigen, die das Programm automatisch findet, bis am Schluss etwas steht wie:
187 Jobs
DONE!
Das bedeutet, dass das Programm mal funktioniert.
1. Den Scraper eine andere Stadt auf willhaben.at durchsuchen lassen
Relativ weit oben in deinem Programmcode findest du in Zeile 28 und 29 die beiden Variablen linkBase und nextLinkPart. Die Variable linkBase sagt dem Programm, dass wir uns grundsätzlich auf der Website willhaben.at bewegen. Die Variable nextLinkPart sagt dem Programm, auf welcher Unterseite es nachschauen soll.
Der Link-Bestandteil „location=Kirchdorf%20an%20der%20Krems“ sagt dem Programm, dass es in Kirchdorf an der Krems suchen soll (%20 bedeutet ein Leerzeichen). Der Bestandteil „region=13978“ bestimmt die allgemeine Region, vermutlich den Bezirk oder das Bundesland. Je nach Programmierung der Website, die wir scrapen wollen, finden wir hier andere Link-Bestandteile, auch „query parameter“ genannt.
Wenn wir jetzt z.B. Jobs in Wels suchen wollen, so können wir den Ortsnamen ersetzen und z.B. „location=Wels“ statt „location=Kirchdorf%20an%20der%20Krems“ in die Variable schreiben. Wie aber finden wir die richtige Zahl für region heraus? Indem wir einfach mit einem Browser (ich empfehle Mozilla Firefox) auf die Website gehen, die unser Scraper selbst auch besucht:
https://www.willhaben.at/jobs/suche?location=Kirchdorf%20an%20der%20Krems®ion=13978
Links sehen wir, dass Kirchdorf an der Krems eingetragen ist.
Wenn wir das auf Wels ändern, ändert sich der Link in der Adresszeile entsprechend:
https://www.willhaben.at/jobs/suche?location=Wels®ion=14087
Wenn du genau schaust, wirst du merken, dass sich der Anfang vom Link nicht verändert hat – nur location und region haben eine anderen Wert.
Wenn du die in deinem Programmcode umschreibst, könnte das dann z.B. so aussehen:
String linkBase = "https://www.willhaben.at"; // Basis-Link OHNE Unterseiten String nextLinkPart = "/jobs/suche?location=Wels®ion=14087"; // Unterseite, die als erstes besucht wird String nextLink = linkBase + nextLinkPart; // baue gesamten Link aus Basis-Link und Unterseite zusammen
Starte dein Programm neu. Nun bekommst du Jobs aus Wels statt welchen aus Kirchdorf – in meinem Fall 331 Jobs.
2. Mehr, andere oder weniger Daten scrapen
Wie funktioniert aber jetzt eigentlich unser Scraper?
Selbst wenn du noch nie (Java) programmiert hast, kannst du einige Dinge selbst verändern. Hier kurz erklärt, was ein Scraper eigentlich macht:
2.1 Daten von Webserver empfangen
Das Programm besucht einen vorher definierten Link (in unserem Fall https://www.willhaben.at/jobs/suche?location=Wels®ion=14087) und speichert den gesamten Text, den der Webserver/die Website zurückschickt, in einer Variable (in unserem Fall heißt diese „doc“). Das Format, in dem ein Webserver Daten schickt, nennt sich HTML (wenn du mehr über HTML wissen willst, findest du hier einige Tutorials zu HTML).
2.2 Aus HTML-Daten die Daten extrahieren, die uns interessieren
HTML ist grundsätzlich verschachtelt aufgebaut. So besteht eine Unterseite zum Beispiel meistens aus einem Header, einem Inhaltsteil und einem Footer. Der Inhaltsteil hat dann wiederum vielleicht einen linken und einen rechten Bereich. Im Linken Bereich finden sich verschiedene Unter-Inhaltsbereiche (z.B. Absätze) und so weiter, z.B. so:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="style.css"> <script src="test.js"></script> </head> <body> <div id="results"> </div> <div id="loading"> <div id="questionMe">Frag mich was...</div> <div id="spinner"></div> </div> <textarea id="llmTest"> Hallo Model, kannst du mich verstehen? </textarea> <button id="chatButton"> Absenden </button> </body> </html>
Wenn wir nun bestimmte Daten oder Texte aus einer Website scrapen („sammeln“) wollen, müssen wir wissen, welche HTML-Bestandteile diese Daten beinhalten. Dazu können wir den sogenannten Website-Inspektor verwenden. Mit Mozilla Firefox funktioniert das so, dass du mit der rechten Maustaste auf einen Website-Bestandteil klickst, der dich interessiert, und dann „Untersuchen“ wählst:
Dann öffnet sich unten (oder in manchen Browsern auch rechts) der sogenannte „Website inspector“. Mit diesem kannst du viele interessante Dinge machen – aber für uns interessant ist hauptsächlich der Bereich links unten, wo HTML steht. Dort sehen wir den Aufbau der Website als HTML-Code, in desem Fall ein <span>-Element mit der Klasse class=“Text-sc-10o2fdq-0″ und noch einigen anderen:
Warum ist das für uns interessant? Weil wir anhand der Klasse unserem Scraper-Programm sagen können, welche Daten er uns scrapen, also holen soll. In diesem Fall interessiert uns jedoch weniger das <span>-Element, sondern das <a>-Element (ein Link) darüber mit der Klasse „sc-ca51e2d8-0“, weil genau dieselbe Klasse haben wir auch in unserem Java-Scraper-Programm. Suche diese Zeile heraus (Tipp: Du kannst STRG + F in IntelliJ verwenden, um nach „Jobname“ zu suchen):
Element job = oneRow.select(".sc-6686be3a-3").last(); // Suche Element mit Jobname in diesem Block
Hier wird aus der Variable oneRow (das ein einzelnes Job-Kärtchen darstellt, dass wiederum mit Hilfe einer Klasse, nämlich „.Box-sc-wfmb7k-0 .sc-6686be3a-1“ ausgewählt wurde) mit Hilfe von .select() und dem Klassennamen ein Element „job“ ausgewählt, indem eben nach „sc-6686be3a-3“ gesucht wird.
Einige Zeilen später findet sich dann der Eintrag, der aus dem job-Element einen String (=Text) mit dem Namen jobName extrahiert:
String jobName = job.text(); // finde tatsächlichen Jobname als Text in dem Element
Wenn wir nun z.B. in unserer Jobliste nur die Firmen auflisten wollen, aber OHNE die Jobs, müssen wir nur entsprechend alle Einträge, die mit „jobName“ oder „job“ zu tun haben, entfernen.
2.3 Die Daten speichern (in einer .csv-Datei in unserem Fall)
Damit die Daten nicht einfach verloren gehen, können wir sie nun z.B. in einer .csv-Datei speichern. So können wir sie später mit Excel öffnen und weiter bearbeiten, etwa sortieren.
Unter deinem src-Ordner im Java-Projekt hast du eine Datei namens jobList.csv – öffne sie mit einem Doppellick – dort ist viel Text, aber man kann es nicht so gut lesen.
Wenn du aber einen Rechtslick darauf machst und dann Open in/Explorer, kommst du direkt zur Datei. Wenn du sie nun mit Excel öffnest, kannst du sie zum Beispiel nach Job-Art sortieren, nach Arbeitgeber, nach Datum, …
3. Weitere mögliche Schritte
Da unser Java-Programm fähig ist, jeden Link zu öffnen, können wir beispielsweise auch Links, die unser Java-Programm auf einer Website findet, erneut öffnen, und die Links, die dort gefunden werden, wieder durchsuchen, und so weiter. So ist es (mit einigem Zeitaufwand) möglich, das ganze Internet zu durchsuchen. Oder beispielsweise auf unserer Job-Übersichts-Seite jeweils Unterseiten zu den einzelnen Jobs zu öffnen, um mehr Informationen herauszufinden, die nur auf den Unterseiten zu finden sind, z.B. das angebotene Gehalt.
4. Die Grenzen des Möglichen
Was die JSoup-Bibliothek in Java leider nicht gut kann, ist mit Websites umzugehen, die nicht aus klassischem HTML bestehen, also z.B. Websites, die mit Hilfe von Javascript dynamisch laufend neu erzeugt werden. Websites, bei denen man z.B. nur dann mehr Einträge sehen kann, wenn man auf einen „Weiter“-Button klickt (und dieser nicht eine neue Seite lädt, sondern mehr Einträge auf der selben), können so nicht gut durchsucht werden.
Nichtsdestotrotz ist mit einem Website-Scraper schon sehr viel möglich. Sie wollen booking.com nachbauen? Das Grundsprinzip ist ähnlich – nur eben viel komplexer 😉