Zurück zum Blog
Anleitungen
Mihai MaximLast updated on May 12, 202611 min read

HTML-Parsing in Java mit Jsoup

HTML-Parsing in Java mit Jsoup
Kurzfassung: Jsoup ist die Standardbibliothek für das Parsen von HTML in Java. Dieser Leitfaden behandelt den gesamten Lebenszyklus (Maven-Einrichtung, Laden eines Dokuments, CSS-Selektoren, DOM-Durchlauf, Extraktion, Änderung und Serialisierung) sowie ein lauffähiges Scraping-Projekt, Fehlerbehandlung, Paginierung und die Grenzen, die einen dazu bewegen, auf einen Headless-Browser oder eine Scraping-API zurückzugreifen.

Wenn Sie HTML innerhalb eines JVM-Dienstes extrahieren oder umschreiben müssen, haben Sie einige Optionen, aber für die meisten praktischen Aufgaben beginnt und endet das HTML-Parsing in Java nach wie vor mit Jsoup. Web-Scraping ist die automatisierte Extraktion von Daten aus dem HTML-Quellcode einer Website, und Jsoup ist die Open-Source-Bibliothek, die diesen Quellcode in ein navigierbares DOM umwandelt, das Sie mit CSS-Selektoren abfragen und direkt ändern können.

Dieses Jsoup-Tutorial richtet sich an fortgeschrittene Java-Entwickler (Backend-Ingenieure, Dateningenieure, SEO- und QA-Mitarbeiter sowie alle, die Content-Migrationen durchführen), die eine praktische Anleitung statt einer Marketing-Übersicht wünschen. Wir behandeln die Maven-Einrichtung, das Laden einer Document aus einer String, Fileoder einer URL, die Konfiguration der HTTP-Anfrage, die Fehlerbehandlung, das Durchlaufen und Auswählen von Elementen, das Extrahieren von Text und Attributen, das Ändern von Knoten sowie die Serialisierung des Ergebnisses zurück in sauberes HTML. Ein vollständig lauffähiges Scraping-Projekt rundet den Artikel ab, einschließlich Hinweisen zur Paginierung und Ratenbegrenzung.

Wir sind auch ehrlich, was die Grenzen angeht: Jsoup führt kein JavaScript aus, wechselt keine IP-Adressen und umgeht keine Anti-Bot-Abwehrmechanismen. Der abschließende Abschnitt zeigt auf, wo die Grenzen liegen und worauf man als Nächstes zurückgreifen sollte.

Warum Jsoup die erste Wahl für HTML-Parsing in Java ist

Wenn sich die benötigten Daten auf einer öffentlichen Webseite befinden und die Seite keine API bietet, schreibt man einen Scraper. Für das HTML-Parsing in Java ist Jsoup seit Jahren der praktische Standard: Open Source, regelmäßige Releases, fundierte Dokumentation und eine flüssige API, die sich sauber aus jQuery oder Vanilla-DOM-JavaScript portieren lässt. Entscheidend ist, dass es beide Hälften des Workflows abdeckt: HTML lesen und HTML schreiben.

Was Jsoup kann und was nicht – auf einen Blick

Jsoup implementiert die WHATWG-HTML5-Spezifikation und parst daher so gut wie jedes Markup – von makellosem bis hin zu wirklich fehlerhaftem – so, wie es ein moderner Browser tun würde. Man erhält einen DOM-Baum, Selektoren im jQuery-Stil sowie Methoden zum Lesen und Schreiben. Was es nicht tut, ist JavaScript auszuführen. Alles, was von einem clientseitigen Framework nach der ersten Antwort eingefügt wird (ein React-Store, verzögert geladene Zeilen, Hydration-gated-Inhalte), ist für Jsoup unsichtbar. Diese Einschränkung bestimmt den Abschnitt über die Grenzen weiter unten.

Einrichten von Jsoup in einem Maven-Projekt

Erstellen Sie ein Maven-Skelett mit mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstartund fügen Sie dann die Jsoup-Abhängigkeit zu pom.xml. Zum Zeitpunkt der Erstellung dieses Artikels ist die aktuelle Version die 1.17.x-Serie, aber überprüfen Sie immer die neueste stabile Version auf Maven Central, bevor Sie eine Version in der Produktion festlegen:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.17.x</version>
</dependency>

Wenn Sie die Beispiele mit mvn exec:javaausführen möchten, registrieren Sie das Exec Maven Plugin in Ihrem <plugins> Block. Verwenden Sie die Version, die auf der Plugin-Seite als aktuell aufgeführt ist; ältere Tutorials verweisen auf 3.0.0, was möglicherweise bereits veraltet ist. Sie verwenden kein Maven? Jsoup wird als einzelne JAR-Datei ausgeliefert, die Sie in den Klassenpfad einfügen können, und Gradle-Benutzer können implementation 'org.jsoup:jsoup:1.17.x'.

HTML in ein Jsoup-Dokument laden

Der Einstiegspunkt für das Parsen von HTML in Java mit dieser Bibliothek ist die Jsoup Klasse. Sie können HTML aus einer String, ein File, einem InputStreamoder (am häufigsten) direkt von einer URL abrufen. Ein sauberes Jsoup-Connect-Beispiel sieht so aus:

// From a string, great for unit tests
Document fromString = Jsoup.parse("<html><body><p>Hello</p></body></html>");

// From a local file
Document fromFile = Jsoup.parse(new File("page.html"), "UTF-8");

// From a live URL: issues the HTTP request, then parses the response
Document doc = Jsoup.connect("https://example.com").get();

Im Vergleich zur eigenen HttpURLConnection, spart die flüssige Jsoup.connect(...) API viel Routinearbeit: Sie verwaltet den Socket, liest den Body, dekodiert den Zeichensatz und gibt einen geparsten Document in einem einzigen Aufruf zurück. Das Document ist das In-Memory-DOM, mit dem Sie für alles andere arbeiten, von CSS-Selektoren bis hin zur DOM-Bearbeitung.

Konfiguration der Jsoup-Verbindung (Header, Cookies, Timeouts, User-Agent)

Jsoup.connect(url) gibt ein Connection Objekt zurück, das du vor dem Absenden der Anfrage konfigurieren kannst. Die Standardeinstellungen sind für benutzerfreundliche Endpunkte in Ordnung, aber die meisten echten Ziele benötigen zumindest einen echten User-Agent und ein sinnvolles Timeout:

Document doc = Jsoup.connect("https://example.com/listing")
    .userAgent("Mozilla/5.0 (compatible; MyJavaScraper/1.0; +https://yourdomain.tld/bot)")
    .referrer("https://example.com")
    .header("Accept-Language", "en-US,en;q=0.9")
    .cookie("session", "abc123")
    .timeout(10_000)
    .data("q", "java")
    .method(Connection.Method.GET)
    .get();

Wählen Sie einen User-Agent, der Ihren Bot ehrlich identifiziert. Viele Server geben eine abgespeckte Antwort zurück oder blockieren den Zugriff gänzlich, wenn der User-Agent wie ein standardmäßiger Java-HTTP-Client aussieht.

Behandlung von HTTP-Fehlern, Statuscodes und Timeouts

Zwei Ausnahmen sind hier von Bedeutung. HttpStatusException wird ausgelöst, wenn der Server einen 4xx- oder 5xx-Code zurückgibt, und liefert dir sowohl die fehlerhafte URL als auch den Statuscode. IOException deckt alles andere ab: DNS-Fehler, Verbindungsabbrüche, Socket-Timeouts. Fangen Sie beides ab:

try {
    Document doc = Jsoup.connect(url).timeout(10_000).get();
} catch (HttpStatusException e) {
    log.warn("Bad status {} for {}", e.getStatusCode(), e.getUrl());
} catch (IOException e) {
    // retry with exponential backoff, then escalate
}

Wenn Sie tatsächlich den Inhalt einer 404-Seite benötigen (zur Erkennung von Soft-404), verketten Sie .ignoreHttpErrors(true) vor .get(). Umschließen Sie Netzwerkaufrufe in einer Wiederholungsschleife mit exponentiellem Backoff für Produktions-Scraper; vorübergehende 5xx- und Verbindungsrücksetzfehler sind bei großem Umfang normal.

Auswählen von Elementen mit Jsoup-CSS-Selektoren

Sobald Sie ein Document, ist die Abfrage mit einem einzigen Befehl erledigt. Document.select(String cssQuery) akzeptiert dieselbe Syntax, die Sie in querySelectorAll und gibt eine Elements Sammlung, die niemals null, selbst wenn nichts passt. Allein dadurch entfällt eine ganze Klasse von NullPointerExceptions, auf die du sonst bei naivem DOM-Code stoßen würdest.

Das Vokabular der Jsoup-CSS-Selektoren geht weit über Tags und Klassen hinaus. Eine kurze Übersicht, die es wert ist, neben jedem CSS-Selektor-Spickzettel als Lesezeichen gespeichert zu werden:

Selektor

Übereinstimmungen

div.post-card

<div> mit Klasse post-card

article > h2

direktem Kind h2 eines article

a[href^=https]

Links, deren href mit https

img[src*=authors]

Bilder, deren src die Teilzeichenfolge authors

li:nth-child(2)

zweite li in ihrem übergeordneten

section:has(h2)

Abschnitten, die mindestens einen h2

p:contains(error)

Absätze enthalten, die den Literaltext „Fehler“ enthalten

Kombinieren Sie diese frei. Ein bewährtes Muster besteht darin, einen untergeordneten Selektor auf einen zuvor ausgewählten Element , anstatt Abfragen vom Dokumentstamm erneut auszuführen.

getElementById, getElementsByClass und select Im Vergleich

Jsoup spiegelt die JavaScript-DOM-API für Leser wider, die explizite Getter wünschen. getElementById(id) gibt das einzelne Element (oder null) mit dieser ID zurück, identisch mit document.getElementById in einem Browser. getElementsByClass(name) gibt alle Übereinstimmungen zurück, genau wie document.getElementsByClassName. select(cssQuery) entspricht querySelectorAll und ist die flexibelste der drei.

Verwenden Sie die expliziten Getter, wenn die Absicht offensichtlich ist (eine stabile ID oder eine einzelne semantische Klasse), und select() wenn Sie Komposition oder Attributfilterung benötigen. Ein praktischer Hinweis: Vermeiden Sie vom Framework generierte Utility-Klassen als Anker-Selektoren. Eine Tailwind-Klasse wie p-[10px] oder text-slate-700 ist ein Detail der Build-Ausgabe, das beim nächsten Deploy verschwinden kann. Setze auf stabile IDs, ARIA-Rollen oder semantische Tags, dann bleiben deine Scraper viel länger aktuell.

Durchlaufen des DOM-Baums: Eltern, Geschwister, Kinder, erstes/letztes/n-tes

Selektoren öffnen dir die Tür; das Durchlaufen führt dich zu Geschwistern und Vorfahren. Die Jsoup-Document-API stellt parent(), parents(), children()und siblingElements()sowie den indizierten Zugriff über first(), last()und get(int n). Jede Element verfügt zudem über eine eigene select() Methode, die eine Abfrage auf diesen Teilbaum beschränkt, was die sauberste Art ist, robuste Selektoren zu schreiben:

Element card = doc.selectFirst("article.post-card");
String title  = card.select("h2 > a").text();
String author = card.parent().select(".byline").text();
Elements tags = card.children().select("span.tag");

Sich zu einem stabilen Vorfahren hochzuarbeiten und wieder zurück ist weitaus robuster als das Verketten von instabilen Klassenselektoren ausgehend von der Dokumentwurzel, insbesondere auf Seiten, die CSS-in-JS oder Utility-Class-Frameworks verwenden.

Extrahieren von Text, HTML und Attributen aus Elementen

Sobald Sie ein Element, decken vier Methoden fast jeden Fall ab, in dem Sie Daten aus HTML in Java extrahieren. text() gibt sichtbaren, umgebrochenen Text zurück (analog zu innerText). html() gibt inneres HTML als Zeichenkette zurück. outerHtml() beinhaltet die eigenen Tags des Elements. ownText() gibt nur die direkten Textknoten des Elements zurück und überspringt Nachkommen.

Für Attribute attr("href") liest einen Wert und absUrl("href") löst relative URLs anhand der Basis-URI des Dokuments auf, was beim Scrapen von Linklisten von unschätzbarem Wert ist. Die Iteration ist unkompliziert, da Elements ist Iterable:

for (Element link : doc.select("a[href]")) {
    System.out.println(link.text() + " -> " + link.absUrl("href"));
}

Sie können auch streamen, forEachoder nach Index abrufen mit get(n). Was auch immer sich für Ihre Codebasis am idiomatischsten anfühlt, ist in Ordnung.

HTML mit Jsoup bearbeiten und ausgeben

Die meisten Tutorials beschränken sich auf die Extraktion, doch das Parsen von HTML in Java mit Jsoup ist tatsächlich bidirektional. Das Gleiche attr(), text()und html() dienen gleichzeitig als Setter. Sie können neue Knoten mit new Element(Tag.valueOf("...")), sie mit appendChild() oder appendElement()und mit remove(). Die Jsoup-Oberfläche zur Bearbeitung von HTML sieht wie folgt aus:

Document doc = Jsoup.parse(rawHtml);

// Edit existing nodes
doc.select("a.tracker").forEach(a -> a.attr("rel", "nofollow"));
doc.selectFirst("h1").text("Updated Title");

// Add a new node
Element note = new Element(Tag.valueOf("p"), "")
    .text("Edited by my scraper at " + Instant.now());
doc.body().appendChild(note);

// Remove ad slots
doc.select("div.ad-slot").remove();

// Serialize back to a clean HTML string
String cleaned = doc.html();

Dieser Rundlauf (Parsen, Modifizieren, Serialisieren) macht Jsoup nützlich für Content-Migrationen, HTML-Sanierung und Feed-Normalisierung, nicht nur für einmaliges Scraping.

Praktisches Projekt: End-to-End-Scraping einer Blog-Liste

Um alles zusammenzufassen, erstellen Sie einen kleinen Scraper, der den Titel, den Link, das Header-Bild und den Autoren-Avatar aus jeder Beitragskarte einer öffentlichen Blog-Liste extrahiert. Öffnen Sie die Seite zunächst in DevTools; manuelle Erkundung ist immer besser als Raten. Identifizieren Sie einen stabilen Container-Selektor pro Karte und schreiben Sie dann Feld-für-Feld-Selektoren dafür.

Document doc = Jsoup.connect("https://example.com/blog")
    .userAgent("MyJavaScraper/1.0")
    .timeout(10_000)
    .get();

for (Element card : doc.select("article.post-card")) {
    String title   = card.select("h2 > a").text();
    String url     = card.select("h2 > a").absUrl("href");
    String header  = card.selectFirst("img.header-image").absUrl("src");
    String avatar  = card.select("img[src*=authors]").attr("abs:src");

    System.out.printf("%s | %s | %s | %s%n", title, url, header, avatar);
}

Jedes Feld hat seinen eigenen absichtsgesteuerten Selektor. img[src*=authors] Es filtert nach Attribut-Teilstrings, was bei Änderungen im Markup besser funktioniert als das Verketten struktureller Selektoren. Diese Art des strukturierten Java-Web-Scrapings mit Jsoup ist jedes Mal besser als instabiles, indexbasiertes Parsing.

Durchlaufen paginierter Seiten

Die meisten Einträge folgen einem vorhersehbaren URL-Schema wie /blog, /blog/page/2/, /blog/page/3/. Behandle Seite 1 als Sonderfall und durchlaufe die Seiten, bis du auf einen leeren Ergebnissatz oder einen 404-Fehler von HttpStatusException. Halte zwischen den Anfragen ein oder zwei Sekunden Pause, variiere die Abfolge leicht und beachte die robots.txt der Zielseite (RFC 9309). Paginierung ohne Ratenbegrenzung ist der schnellste Weg, gesperrt zu werden, und der häufigste Grund, warum Leute am Ende Artikel darüber lesen, warum man gesperrt wird.

Einschränkungen von Jsoup und wann man nach einer Alternative suchen sollte

Die harte Grenze von Jsoup ist JavaScript. Es analysiert nur das, was der Server ursprünglich zurückgibt, sodass alles, was clientseitig gerendert wird (React-, Vue- oder Angular-SPAs, lazy-geladener Infinite Scroll, hinter Hydration gesperrte Inhalte), unsichtbar bleibt. Es bietet zudem keine Unterstützung für Headless-Rendering, Proxy-Rotation oder Anti-Bot-Bypass.

Wenn die Seite dynamisch ist, kombiniere Jsoup mit einem Headless-Browser: Selenium und Playwright steuern ein echtes Chromium; HtmlUnit ist eine schlankere, JVM-native Option; Jaunt bietet eine ähnliche Java-API mit integriertem HTTP. Wenn die Seite statisch, aber feindselig ist (Cloudflare, häufige IP-Sperren, Fingerprinting), leiten Sie die Anfrage über eine verwaltete Scraping-API, die Proxys und CAPTCHAs verarbeitet, und speisen Sie die Antwort-HTML direkt zurück in Jsoup ein. Das hält Ihren Parsing-Code sauber und reduziert bewegliche Teile.

Zusammenfassung: Aufbau robuster Java-HTML-Parser

Der gesamte Workflow für das HTML-Parsing in Java mit Jsoup besteht aus vier Schritten: Laden, Auswählen, Extrahieren oder Modifizieren und schließlich Ausgeben. Für weiterführende Informationen sind das Jsoup-Cookbook und die Javadocs die maßgeblichen Referenzen. Bevor Sie einen neuen Scraper erstellen, gehen Sie eine kurze Entscheidungscheckliste durch: Ist die Seite statisch oder wird sie mit JS gerendert? Ist es wahrscheinlich, dass das Ziel mich blockiert? Muss ich HTML verändern oder nur lesen? Diese drei Antworten zeigen Ihnen, ob Jsoup allein ausreicht.

Wichtige Erkenntnisse

  • Verwenden Sie Jsoup für alle HTML-Parsing-Aufgaben in Java, bei denen das Markup vom Server gerendert wird. Es behandelt fehlerhaftes HTML so, wie es ein moderner Browser tun würde.
  • Jsoup.connect(url).get() Es fasst das Abrufen und Parsen in einem Aufruf zusammen. Lege immer einen echten User-Agent und ein nicht standardmäßiges Timeout fest und fange beide HttpStatusException und IOException.
  • select() gibt eine Elements Liste, die zwar leer sein kann, aber niemals null. Bevorzugen Sie stabile IDs, ARIA-Rollen und semantische Selektoren gegenüber vom Framework generierten Utility-Klassen.
  • Jsoup ist bidirektional: attr, textund html als Setter sowie appendChild und remove, ermöglichen es dir, HTML zu bearbeiten und neu zu serialisieren, nicht nur zu lesen.
  • Jsoup führt kein JavaScript aus. Kombinieren Sie es für SPAs mit Selenium, Playwright oder HtmlUnit; leiten Sie die Anfrage für blockierte Ziele über eine verwaltete Scraping-API weiter.

FAQ

Kann Jsoup JavaScript-gerenderte oder Single-Page-Apps scrapen?

Nein. Jsoup analysiert nur den rohen HTML-Code, den der Server zurückgibt, sodass alles, was nach dem Laden der Seite von einem clientseitigen Framework generiert wird, für Jsoup unsichtbar ist. Um SPAs oder Seiten zu scrapen, die auf dem Client geladen werden, steuern Sie einen echten oder headless Browser mit Selenium, Playwright oder HtmlUnit an, erfassen Sie den vollständig gerenderten HTML-Code und übergeben Sie diesen String dann an Jsoup.parse(...) für die selektorbasierte Extraktion.

Wie unterscheidet sich Jsoup von HtmlUnit, Jaunt oder Selenium beim HTML-Parsing?

Jsoup ist ein reiner HTML-Parser. Es führt kein JavaScript aus, betreibt keine JS-Engine und simuliert keinen Browser. HtmlUnit und Selenium rendern beide Seiten mit einer JS-Engine (HtmlUnit innerhalb der JVM, Selenium über einen echten Browser-Treiber). Jaunt ist als Parser plus einfacher HTTP-Client näher an Jsoup angesiedelt. Verwenden Sie Jsoup, wenn die Seite statisch ist; verwenden Sie die anderen, wenn Sie Rendering oder Interaktion benötigen.

Wie vermeide ich, beim Parsen von Seiten mit Jsoup blockiert oder in der Rate begrenzt zu werden?

Identifizieren Sie Ihren Bot ehrlich im User-Agent, drosseln Sie Anfragen auf wenige pro Sekunde pro Host, randomisieren Sie Verzögerungen und verwenden Sie Cookies, wo dies sinnvoll ist. Lesen und beachten Sie robots.txt. Bei Aufträgen mit hohem Volumen oder bei feindseligen Zielen leiten Sie Anfragen über einen privaten oder rotierenden Proxy-Pool weiter, da Jsoup selbst keine integrierte IP-Rotation, Fingerabdruck-Spoofing oder CAPTCHA-Verarbeitung bietet.

Kann Jsoup XML, RSS-Feeds oder fehlerhaftes HTML parsen?

Ja zu allen drei Punkten. Übergeben Sie einen XML-Parser explizit mit Jsoup.parse(input, baseUri, Parser.xmlParser()) für RSS-Feeds, Sitemaps und andere XML-Dokumente. Bei fehlerhaftem HTML ist der Standard-Parser tolerant und normalisiert das Markup so, wie es ein moderner Browser tun würde, sodass nicht geschlossene Tags und verstreute Zeichen in der Regel immer noch ein brauchbares Document.

Was ist die aktuellste stabile Jsoup-Version und wie halte ich sie auf dem neuesten Stand?

Schau direkt bei Maven Central nach, da sich die Versionsnummern häufig ändern und jede in einem Tutorial angegebene Nummer möglicherweise bereits veraltet ist. Abonniere die Release Notes im Jsoup-GitHub-Repository oder führe ein Maven-Plugin zur Aktualisierung von Abhängigkeiten aus, wie z. B. versions:display-dependency-updates in CI aus, um verfügbare Upgrades automatisch anzuzeigen. Renovate und Dependabot funktionieren beide, sofern Ihr Repo entsprechend gehostet wird.

Fazit

Wenn du diesen Leitfaden durcharbeitest und dir nur eine Sache merkst, dann soll es der Vier-Schritte-Rhythmus sein: Lade HTML in ein Document, wählen Sie aus, was Sie interessiert, extrahieren oder modifizieren Sie es und serialisieren Sie es wieder zurück. Diese Abfolge ist das Rückgrat jedes Jsoup-basierten Scrapers, jeder Content-Migration und jedes HTML-Sanitisers, den Sie schreiben werden. Fügen Sie einen echten User-Agent, vernünftige Timeouts, strukturierte Ausnahmebehandlung und eine Retry-Richtlinie mit Backoff hinzu, und Sie haben einen Parser, der den Produktionsverkehr übersteht.

Die ehrliche Einschränkung gilt nach wie vor: Jsoup führt kein JavaScript aus und umgeht keine Anti-Bot-Abwehrmechanismen. Wenn die Seite clientseitig gerendert wird, benötigen Sie einen Headless-Browser. Wenn das Ziel Ihre IP blockiert oder Ihren Fetcher identifiziert, benötigen Sie eine intelligentere Anforderungsschicht.

Im zweiten Fall macht sich eine verwaltete Scraping-API bezahlt. Die Scraper-API von WebScrapingAPI gibt selbst bei feindseligen Zielen den rohen HTML-Code zurück und kümmert sich dabei selbst um Proxy-Rotation, CAPTCHAs und Browser-Fingerprinting, sodass Sie Ihren Jsoup-Parsing-Code unverändert lassen und lediglich den Abrufschritt austauschen können. Dies ist der sauberste Weg, den wir gefunden haben, um einem schlanken Java-Parser Produktionsresilienz zu verleihen.

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

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

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.