Zurück zum Blog
Leitfäden
Mihai Maxim31. Januar 202310 Minuten Lesezeit

JSoup: HTML-Parsing in Java

JSoup: HTML-Parsing in Java

Einführung in JSoup

Web Scraping kann man sich wie eine digitale Schatzsuche vorstellen. Sie gehen durch eine Website und graben alle Informationen aus, die Sie brauchen. Diese Technik wird für alle möglichen Dinge eingesetzt, z. B. um die günstigsten Preise zu finden, die Stimmung der Kunden zu analysieren oder Daten für die Forschung zu sammeln.

Java gilt als hervorragende Programmiersprache für Web-Scraping, da sie über eine Vielzahl von Bibliotheken und Frameworks verfügt, die bei diesem Prozess helfen können. Eine der bekanntesten Bibliotheken für Web Scraping in Java ist JSoup. Mit JSoup können Sie durch den HTML-Code einer Website navigieren und suchen und alle benötigten Daten extrahieren.

Durch die Kombination von Java mit JSoup können Sie großartige Web-Scraping-Anwendungen erstellen, mit denen Sie schnell und einfach Daten aus Websites extrahieren können. In diesem Artikel führe ich Sie durch die Grundlagen des Web Scraping mit JSoup.

Einrichten eines JSoup-Projekts

In diesem Abschnitt werden wir ein neues Java-Projekt mit Maven erstellen und es so konfigurieren, dass es von der Kommandozeile aus mit dem exec-maven-plugin ausgeführt werden kann. Dadurch können Sie Ihr Projekt einfach verpacken und auf einem Server ausführen, was die Automatisierung und Skalierbarkeit des Datenextraktionsprozesses ermöglicht. Danach werden wir die JSoup-Bibliothek installieren.

Erstellen eines Maven-Projekts

Maven ist ein Build-Automatisierungswerkzeug für Java-Projekte. Es verwaltet Abhängigkeiten, Builds und Dokumentation und erleichtert so die Verwaltung komplexer Java-Projekte. Mit Maven können Sie den Build-Prozess, die Abhängigkeiten und die Dokumentation Ihres Projekts einfach verwalten und organisieren. Außerdem ermöglicht es eine einfache Integration mit Tools und Frameworks.

Die Installation von Maven ist ein einfacher Prozess, der in wenigen Schritten durchgeführt werden kann.

Laden Sie zunächst die neueste Version von Maven von der offiziellen Website(https://maven.apache.org/download.cgi) herunter.

Sobald der Download abgeschlossen ist, entpacken Sie den Inhalt des Archivs in ein Verzeichnis Ihrer Wahl.

Als nächstes müssen Sie die Umgebungsvariablen einrichten.

Unter Windows setzen Sie die Variable JAVA_HOME auf den Speicherort Ihres JDK und fügen den bin-Ordner der Maven-Installation zur PATH-Variablen hinzu.

Unter Linux/macOS müssen Sie die folgenden Zeilen zu Ihrer ~/.bashrc- oder ~/.bash_profile-Datei hinzufügen:

export JAVA_HOME=Pfad/zum/JDK

export PATH=$PATH:Pfad/zu/maven/bin

Bestätigen Sie die Maven-Installation, indem Sie mvn --version in einem Terminal ausführen.

Wenn Sie Maven installiert haben, können Sie nun ein neues Java Maven-Projekt erstellen:

 mvn archetype:generate -DgroupId=com.project.scraper 

-DartifactId=jsoup-scraper-project 

-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Dadurch wird ein neuer Ordner namens "jsoup-scraper-project" erstellt, der den Inhalt des Projekts enthält.

Der Einstiegspunkt für die Anwendung (die Hauptklasse) wird im Paket "com.project.scraper" liegen.

Ausführen des Projekts über die Befehlszeile

Um ein Maven-Java-Projekt von der Kommandozeile aus zu starten, werden wir das exec-maven-plugin verwenden.

To install the plugin, you need to add it to the project's pom.xml file. This can be done by adding the following code snippet to the <build><plugins> section of the pom.xml file:

<build>

 <plugins>

   <plugin>

     <groupId>org.codehaus.mojo</groupId>

     <artifactId>exec-maven-plugin</artifactId>

     <version>3.1.0</version>

     <executions>

       <execution>

         <goals>

           <goal>java</goal>

         </goals>

       </execution>

     </executions>

     <configuration>

       <mainClass>com.project.scraper.App</mainClass>

     </configuration>

   </plugin>

 </plugins>

</build>

Stellen Sie sicher, dass Sie den richtigen Pfad für die Hauptklasse des Projekts auswählen.

Verwenden Sie mvn package exec:java im Terminal (im Projektverzeichnis), um das Projekt auszuführen.

Installation der JSoup-Bibliothek

Um die JSoup-Bibliothek zu installieren, fügen Sie die folgende Abhängigkeit zur pom.xml-Datei Ihres Projekts hinzu:

<dependency>

 <groupId>org.jsoup</groupId>

 <artifactId>jsoup</artifactId>

 <version>1.14.3</version>

</dependency>

Besuchen Sie https://mvnrepository.com/artifact/org.jsoup/jsoup, um die neueste Version zu prüfen.

Parsing von HTML in Java mit JSoup

In diesem Abschnitt werden wir die Website https://www.scrapethissite.com/pages/forms/ untersuchen und sehen, wie wir die Informationen über Eishockeymannschaften extrahieren können. Durch die Untersuchung einer realen Website werden Sie die Konzepte und Techniken verstehen, die beim Web Scraping mit JSoup verwendet werden und wie Sie diese auf Ihre eigenen Projekte anwenden können.

Abrufen der HTML-Datei

Um den HTML-Code von der Website abzurufen, müssen Sie eine HTTP-Anfrage an die Website stellen. In JSoup wird die Methode connect() verwendet, um eine Verbindung zu einer bestimmten URL herzustellen. Sie gibt ein Connection-Objekt zurück, das zur Konfiguration der Anfrage und zum Abrufen der Antwort vom Server verwendet werden kann.

Sehen wir uns an, wie wir die Methode connect() verwenden können, um den HTML-Code von unserer URL abzurufen und ihn dann in eine lokale HTML-Datei (hockey.html) zu schreiben:

package com.project.scraper;

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import java.io.*;

import java.io.IOException;

public class App

{

   public static void main( String[] args )

   {

       String RAW_HTML;

       try {

           Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")

                   .get();

           RAW_HTML = document.html();

           FileWriter writer = new FileWriter("hockey.html");

           writer.write(RAW_HTML);

           writer.close();

       } catch (IOException e) {

           e.printStackTrace();

       }

   }

}

Jetzt können wir die Datei öffnen und die Struktur der HTML-Datei mit den Entwicklertools untersuchen:

Webseite mit einer HTML-Tabelle zu den Statistiken einer Eishockeymannschaft, neben der die Entwicklertools des Browsers geöffnet sind

Die Daten, die wir benötigen, befinden sich in einer HTML-Tabelle auf der Seite. Nachdem wir nun auf die Seite zugegriffen haben, können wir mit Hilfe von Selektoren den Inhalt aus der Tabelle extrahieren.

Selektoren schreiben

Die Selektoren in JSoup haben Ähnlichkeiten mit den Selektoren in JavaScript. Beide haben eine ähnliche Syntax und ermöglichen die Auswahl von Elementen aus einem HTML-Dokument auf der Grundlage ihres Tag-Namens, ihrer Klasse, ihrer id und ihrer CSS-Eigenschaften.

Hier sind einige der wichtigsten Selektoren, die Sie mit JSoup verwenden können:

  • getElementsByTag(): Wählt Elemente anhand ihres Tag-Namens aus.
  • getElementsByClass(): Wählt Elemente anhand ihres Klassennamens aus.
  • getElementById(): Wählt ein Element anhand seiner ID aus.
  • select(): Wählt Elemente basierend auf einem CSS-Selektor aus (ähnlich wie querySelectorAll)

Verwenden wir nun einige von ihnen, um alle Teamnamen zu extrahieren:

try {

   Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")

           .get();

   Elements rows = document.getElementsByTag("tr");

   for(Element row : rows) {

      

       Elements teamName = row.getElementsByClass("name");

      

       if(teamName.text().compareTo("") != 0)

           System.out.println(teamName.text());

      

   }

} catch (IOException e) {

   e.printStackTrace();

}

// Prints the team names:

Boston Bruins

Buffalo Sabres

Calgary Flames

Chicago Blackhawks

Detroit Red Wings

Edmonton Oilers

Hartford Whalers

...

Wir haben jede Zeile durchlaufen und für jede Zeile den Namen der Mannschaft mit dem Klassenselektor "name" ausgegeben.

Das letzte Beispiel unterstreicht die Flexibilität und die Möglichkeit, Selektormethoden mehrfach auf die extrahierten Elemente anzuwenden. Dies ist besonders nützlich, wenn es um komplexe und große HTML-Dokumente geht.

Hier ist eine andere Version, die Java-Streams und die select()-Methode verwendet, um alle Teamnamen zu drucken:

try {

   Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")

           .get();

   Elements teamNamesElements = document.select("table .team .name");

   String[] teamNames = teamNamesElements.stream()

                                         .map(element -> element.text())

                                         .toArray(String[]::new);

   for (String teamName : teamNames) {

       System.out.println(teamName);

   }

} catch (IOException e) {

   e.printStackTrace();

}

// Also prints the team names:

Boston Bruins

Buffalo Sabres

Calgary Flames

...

Drucken wir nun alle Tabellenköpfe und -zeilen aus:

try {

   Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")

           .get();

   Elements tableHeadersElements = document.select("table th");

   Elements tableRowsElements = document.select("table .team");

   String[] tableHeaders =

   tableHeadersElements.stream()

                       .map(element -> element.text())

                       .toArray(String[]::new);

   String[][] tableRows =

   tableRowsElements.stream()

            .map(

                table_row -> table_row

                .select("td")

                .stream()

                .map(row_element -> row_element.text())

                .toArray(String[]::new)

               )

            .toArray(String[][]::new);

   for (int i = 0; i < tableHeaders.length; i++) {

       System.out.print(tableHeaders[i] + " ");

   }

   for (int i = 0; i < tableRows.length; i++) {

       for (int j = 0; j < tableRows[i].length; j++) {

           System.out.print(tableRows[i][j] + " ");

       }

       System.out.println();

   }

} catch (IOException e) {

   e.printStackTrace();

}

// Prints

Team Name Year Wins Losses OT Losses Win ...

Boston Bruins 1990 44 24  0.55 299 264 35 

Buffalo Sabres 1990 31 30  0.388 292 278 14 

Calgary Flames 1990 46 26  0.575 344 263 81 

Chicago Blackhawks 1990 49 23  0.613 284 211 73 

Detroit Red Wings 1990 34 38  0.425 273 298 -25

...

Beachten Sie, dass wir zum Speichern der Zeilen Streams verwendet haben. Hier ist eine einfachere Möglichkeit, dies mit for-Schleifen zu tun:

String[][] tableRows = new String[tableRowsElements.size()][];

for (int i = 0; i < tableRowsElements.size(); i++) {

   Element table_row = tableRowsElements.get(i);

   Elements tableDataElements = table_row.select("td");

   String[] rowData = new String[tableDataElements.size()];

   for (int j = 0; j < tableDataElements.size(); j++) {

       Element row_element = tableDataElements.get(j);

       String text = row_element.text();

       rowData[j] = text;

   }

   tableRows[i] = rowData;

}

Handhabung der Paginierung

Bei der Extraktion von Daten aus einer Website ist es üblich, dass die Informationen über mehrere Seiten verteilt sind. Um alle relevanten Daten abzurufen, müssen Anfragen an jede Seite der Website gestellt und die Informationen von jeder Seite extrahiert werden. Wir können diese Funktion problemlos in unser Projekt integrieren.

Statistiktabelle mit Seitenumbruch auf scrapethissite.com, mit Links zu den Seitenzahlen unterhalb der Tabelle

Alles, was wir tun müssen, ist, den Abfrageparameter page_num in der URL zu ändern und eine weitere HTTP-Anfrage mit der Methode connect() zu stellen.

int pageLimit = 25;

String [] tableHeaders = new String[0];

Vector<String[][]> rowsGroups = new Vector<String [][]>();

for (int currentPage=1; currentPage<pageLimit; currentPage++) {

   try {

       Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)

               .get();

       if(currentPage == 1) {

           Elements tableHeadersElements = document.select("table th");

           tableHeaders = tableHeadersElements.stream()

                   .map(element -> element.text())

                   .toArray(String[]::new);

       }

       Elements tableRowsElements = document.select("table .team");

       String[][] tableRows = new String[tableRowsElements.size()][];

       for (int i = 0; i < tableRowsElements.size(); i++) {

           Element table_row = tableRowsElements.get(i);

           Elements tableDataElements = table_row.select("td");

           String[] rowData = new String[tableDataElements.size()];

           for (int j = 0; j < tableDataElements.size(); j++) {

               Element row_element = tableDataElements.get(j);

               String text = row_element.text();

               rowData[j] = text;

           }

           tableRows[i] = rowData;

       }

       rowsGroups.add(tableRows);

   } catch (IOException e) {

       e.printStackTrace();

   }

   // do something with the headers and the the table rows groups

}

Da die Tabellen auf jeder Seite die gleichen Überschriften haben, sollten Sie darauf achten, dass Sie sie nicht mehrfach abrufen.

Der vollständige Code

Hier ist der vollständige Code, der alle Tabellen von der Website https://www.scrapethissite.com/pages/forms/ extrahiert. Ich habe auch eine Funktion hinzugefügt, die die Daten im CSV-Format speichert:

package com.project.scraper;

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import java.io.*;

import java.io.IOException;

import java.util.Vector;

public class App

{

   public static void main( String[] args )

   {

       int pageLimit = 25;

       String [] tableHeaders = new String[0];

       Vector<String[][]> rowsGroups = new Vector<String [][]>();

       for (int currentPage=1; currentPage<pageLimit; currentPage++) {

           try {

               Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)

                       .get();

               if(currentPage == 1) {

                   Elements tableHeadersElements = document.select("table th");

                   tableHeaders = tableHeadersElements.stream()

                           .map(element -> element.text())

                           .toArray(String[]::new);

               }

               Elements tableRowsElements = document.select("table .team");

               String[][] tableRows = new String[tableRowsElements.size()][];

               for (int i = 0; i < tableRowsElements.size(); i++) {

                   Element table_row = tableRowsElements.get(i);

                   Elements tableDataElements = table_row.select("td");

                   String[] rowData = new String[tableDataElements.size()];

                   for (int j = 0; j < tableDataElements.size(); j++) {

                       Element row_element = tableDataElements.get(j);

                       String text = row_element.text();

                       rowData[j] = text;

                   }

                   tableRows[i] = rowData;

               }

               rowsGroups.add(tableRows);

           } catch (IOException e) {

               e.printStackTrace();

           }

       }

       writeFullTableToCSV(rowsGroups, tableHeaders, "full_table.csv");

   }

   public static void writeFullTableToCSV(Vector<String[][]> rowsGroups, String[] headers, String fileName) {

       File file = new File(fileName);

       try {

           FileWriter writer = new FileWriter(file);

           // write the headers first

           for (int i = 0; i < headers.length; i++) {

               writer.append(headers[i]);

               if (i != headers.length - 1) {

                   writer.append(",");

               }

           }

           writer.append("\n");

           // write all the rows groups

           for (String [][] rowsGroup : rowsGroups) {

               for (String[] row : rowsGroup) {

                   for (int i = 0; i < row.length; i++) {

                       writer.append(row[i]);

                       if (i != row.length - 1) {

                           writer.append(",");

                       }

                   }

                   writer.append("\n");

               }

           }

           writer.flush();

           writer.close();

       } catch (IOException e) {

           e.printStackTrace();

       }

   }

}

Einpacken

In diesem Artikel haben wir uns mit der Installation von Maven und der Erstellung eines neuen Java-Maven-Projekts sowie mit der Ausführung des Projekts von der Kommandozeile aus beschäftigt. Wir haben auch besprochen, wie man die JSoup-Bibliothek installiert, indem man die Abhängigkeit zur pom.xml-Datei des Projekts hinzufügt. Schließlich haben wir uns ein Beispiel für die Verwendung von JSoup zum Parsen von HTML und Extrahieren von Daten aus einer Website angesehen. Wenn Sie die in diesem Artikel beschriebenen Schritte befolgen, sollten Sie eine solide Grundlage für die Einrichtung eines JSoup-Projekts haben und mit der Extraktion von Daten aus Websites beginnen können. JSoup bietet eine breite Palette von Optionen und Möglichkeiten für Web Scraping, und ich möchte Sie ermutigen, sie zu erforschen und auf Ihre eigenen Projekte anzuwenden.

Wie Sie gesehen haben, werden Daten oft über mehrere Webseiten verteilt. Schnelle Anfragen an dieselbe Domain können dazu führen, dass Ihre IP gesperrt wird. Mit unserem Produkt, WebScrapingAPI, müssen Sie sich über solche Probleme keine Gedanken machen. Unsere API sorgt dafür, dass Sie so viele Anfragen stellen können, wie Sie benötigen. Und das Beste daran ist, dass Sie es kostenlos ausprobieren können.

Über den Autor
Mihai Maxim, Full-Stack-Entwickler bei WebScrapingAPI
Mihai MaximFull-Stack-Entwickler

Mihai Maxim ist Full-Stack-Entwickler bei WebScrapingAPI, wo er in allen Bereichen des Produkts mitwirkt und an der Entwicklung zuverlässiger Tools und Funktionen für die Plattform mitwirkt.

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.