Apache Nutch ist ein Crawler der Apache Software Foundation, dessen Entwicklung 2004 begonnen hat. Es ist ein JVM-System auf Basis von Hadoop und läuft nicht als einzelner Streaming-Befehl, sondern als Schleife: Seed-URLs werden per inject in eine persistente Datenbank geschrieben, danach folgen die Runden generate → fetch → parse → updatedb. Für Protokoll, Parser, URL-Filter und Scoring gibt es jeweils Plugin-Slots. Die übliche Ausgabe landet nicht als CSV, sondern in einem Suchindex wie Solr oder Elasticsearch.
Ich habe Nutch 1.22 gegen eine kontrollierte lokale Testseite laufen lassen — eine Seite, die jede Anfrage serverseitig protokolliert, sodass die Ergebnisse danach beurteilt werden, was der Server tatsächlich gesehen hat, nicht danach, was der Crawler behauptet. Vier Grenzen haben den Lauf geprägt: die JDK-Version, http.agent.name, der Crawl-Umfang und die Frage, ob parse-js in plugin.includes enthalten ist. Der komplette Zyklus lief mit der getesteten Konfiguration mehrfach durch; änderte ich das JDK oder ließ die Agentenkennung leer, stoppte der Crawl noch vor dem Abruf sinnvoller Seiten.
Die JDK-Grenze zeigt sich noch vor dem eigentlichen Crawl. Nutch 1.22 startete hier auf JDK 26.0.1 nicht: Der erste Hadoop-Job starb in Subject.getSubject(), nachdem Java den SecurityManager-Pfad entfernt hatte. Nutch bringt Hadoop 3.4.2 mit; der Fix landete erst sieben Tage nach dem Release von Nutch 1.22 in Hadoop 3.4.3. Unabhängig davon verbesserte parse-js die Erkennung zweier JavaScript-Dateiliterale von 0/2 auf 2/2, ganz ohne Browser-Ausführung.
Wofür Nutch gedacht ist — und wofür nicht
Nutch ist kein klassischer Scraper. Strukturiertes Extrahieren einzelner Felder gehört nicht zu seiner Aufgabe: Er entdeckt und lädt URLs in großem Umfang, verwaltet dazu eine persistente Datenbank dieser URLs und ihrer Zustände (die crawldb) und übergibt Segmente an ein anderes System, das daraus einen Index macht. Wer ihm einen Katalog vorlegt und eine Tabelle mit Namen und Preisen erwartet, bekommt stattdessen eine crawldb.
Diese Architektur erklärt fast alles, was folgt. Nutch stammt aus einer Zeit, in der Single-Binary-Crawler noch nicht die Norm waren, und es ist für genau das Problem gebaut, für das Hadoop geschaffen wurde: mehr Seiten crawlen, als auf eine einzelne Maschine passen. Einen Laptop mit einem 12-Seiten-Testset damit zu beschäftigen, ist wie einen Güterzug zu mieten, um ein Bücherregal zu transportieren — aufschlussreich für den Zug, aber unfair, wenn man Fahrrad-Handling erwartet.
Die aktuelle Version ist 1.22, veröffentlicht am 17. Februar 2026. Die Lizenz ist Apache-2.0, das Repo hatte bei meiner Prüfung am 27. Juli 2026 3.272 Sterne und 8 offene Issues, und der Hauptzweig war vier Tage zuvor zuletzt gepusht worden. Das ist ein gepflegtes Projekt, kein verlassenes — und genau so sollte man das JDK-Problem lesen: als Paketierungsfenster, das eine Woche zu früh geschlossen wurde, nicht als Vernachlässigung.
Die Versionsmatrix: JDK 24+, Hadoop 3.4.2 und ein Fix mit zwei Zeilen
Die Blockade ist das Ergebnis eines Dreiklangs aus Versionen, und der einzige Teil davon, den Sie beeinflussen können, ist das JDK, auf dem Nutch läuft. Der allererste Hadoop-Job scheiterte auf dem Standard-JDK des Systems bereits beim Start:
java.lang.UnsupportedOperationException: getSubject is not supported
at java.base/javax.security.auth.Subject.getSubject(Subject.java:277)
at org.apache.hadoop.security.UserGroupInformation.getCurrentUser(UserGroupInformation.java:588)
at org.apache.nutch.crawl.Injector.inject(Injector.java:473)
Rückgabecode 255. Keine Seite wurde abgerufen. bin/nutch inject kommt nicht einmal bis ins Netzwerk — es initialisiert einen Hadoop-LocalJobRunner, der wissen will, wer der aktuelle Benutzer ist; das ruft Subject.getSubject() auf, und JEP 486 machte daraus ab JDK 24 eine zwingende Ausnahme, weil Java den SecurityManager dauerhaft entfernt hat. Mein Host lief auf OpenJDK 26.0.1, also weit hinter dieser Schwelle.
Auch der klassische Ausweg hilft nicht. Das Hinzufügen von -Djava.security.manager=allow, früher der Schalter für das alte Verhalten, wird schon vom VM-Start verweigert, bevor Nutch-Code überhaupt geladen wird:
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security
Manager. Enabling a Security Manager is not supported.
Das ist Rückgabecode 1 und bewusst ein Sackgasse — der Schalter wurde zusammen mit der Funktion entfernt.
Die eigentliche Ursache liegt in der Hadoop-Version, die Nutch 1.22 bündelt. Das getSubject-Problem ist als HADOOP-19212 dokumentiert und in Hadoop 3.4.3 und 3.5.0 behoben; Nutch 1.22 bringt hadoop-common-3.4.2 mit. Nutch 1.22 erschien am 17. Februar 2026, und Hadoop 3.4.3 folgte etwa eine Woche später.
Das ist auch kein Problem mit einer Solr- oder Hadoop-Cluster-Abhängigkeit. Oft wird angenommen, Nutch brauche zwingend ein Hadoop-Cluster und laufendes Solr, um überhaupt etwas zu tun. Das stimmt nicht. Der Local Mode nutzt Hadoops in-process-LocalJobRunner — kein HDFS-Daemon, kein YARN, kein Cluster. Der komplette Zyklus inject → generate → fetch → parse → updatedb läuft auf einer einzigen Maschine, ohne dass sonst etwas installiert sein muss. Die JDK-Mauer ist rein ein Problem der gebündelten Bibliotheksversion und stoppt Sie, bevor diese Infrastrukturfrage überhaupt relevant wird.
Die praktische Versionsmatrix, alles dreifach gemessen:
| Verwendetes JDK | Befehl | Ergebnis |
|---|---|---|
| OpenJDK 26.0.1 | bin/nutch inject | Fehlgeschlagen, rc=255 — UnsupportedOperationException: getSubject is not supported |
| OpenJDK 26.0.1 | bin/nutch inject + -Djava.security.manager=allow | Fehlgeschlagen, rc=1 — VM startet nicht |
| OpenJDK 17.0.20 (LTS) | bin/nutch inject | Funktioniert, rc=0 — Total new urls injected: 1 |
Die Lösung besteht aus zwei Befehlen: ein LTS-JDK installieren und Nutch darauf zeigen lassen:
brew install openjdk@17
export NUTCH_JAVA_HOME=/opt/homebrew/opt/openjdk@17
Als keg-only-Installation greift das nicht ins System-Standard-JDK ein. Die eigene CI von Nutch zielt auf Java 17, und das Projekt hat öffentlich angekündigt , dass 1.22 die letzte Version ist, die auf Java 11 läuft, und dass 1.23 Java 17 voraussetzen wird. Ein LTS-JDK ist also kein Workaround, sondern die unterstützte Konfiguration. Die Diskrepanz liegt zwischen dem, was Nutch unterstützt, und dem, was brew install openjdk Ihnen 2026 liefert — zwei verschiedene Fragen, die ausgerechnet beim allerersten Befehl kollidieren.
Alles, was danach kam, lief auf OpenJDK 17.0.20, und dort ist der gesamte Zyklus sauber.
Aufbau, gemessen: 396 MB und eine Eigenschaft, die alles blockiert
„Schwergewichtig“ ist das Wort, zu dem jeder greift — und ohne Messung ist es wertlos. Das steckt tatsächlich in der entpackten Binary-Distribution von Nutch 1.22:
| Element | Nutch 1.22 Binary-Distribution |
|---|---|
| Entpackte Größe | ≈396 MB |
JARs in lib/ | 188 (≈113 MB) |
| — davon der gebündelte Hadoop-Stack | 13 |
| Plugin-Verzeichnisse | 78 |
| JARs in diesen Plugin-Verzeichnissen | 533 |
| Konfigurationsdateien | 35 |
Skripte in bin/ | 2 — crawl und nutch |
Zum Vergleich: Ein moderner Go-Crawler wie katana liefert ein einziges ~50-MB-Binary ohne JVM und ohne externe JARs aus.
Dann gibt es noch die eine Hürde, vor der Sie niemand warnt. Das ausgelieferte nutch-site.xml ist leer, und http.agent.name steht standardmäßig auf einem leeren String. Bleibt es unset, holte mein erster Crawl null Pfade und protokollierte:
ERROR Fetcher: No agents listed in 'http.agent.name' property.
Allein diese eine Eigenschaft zu setzen — sonst nichts — machte daraus einen funktionierenden Fetch. War das Feld leer, beendete der Befehl den Lauf ohne Seitenabruf und das Log meldete die obige Agenten-Fehlermeldung; es blieb also nicht still.
Die minimal nötige Konfiguration bestand am Ende aus drei Dateien: conf/nutch-site.xml (Agentenname, Plugin-Set, Scope), conf/regex-urlfilter.txt (Host-Scoping) und einer Seed-URL-Datei. Das ist kein großer Aufwand — nur eben drei Dateien mehr als crawler run <url>.
Was es gefunden hat: Der entscheidende Plugin-Schalter

Die Testseite enthielt drei bewusst unterschiedliche Endpoint-Typen, und Nutchs Verhalten trennte sich entlang dieser Klassen sauber auf:
- Klasse A — gewöhnliche HTML-Links (4 Seiten, plus eine Kette mit 3 Link-Tiefen)
- Klasse B — Endpunkte, die nur als String-Literale in einer verlinkten JavaScript-Datei existieren: einmal als Funktionsargument,
fetch('/api/js-endpoint-7'), und einmal als Zuweisung,const other = "/api/js-endpoint-8" - Klasse C — ein Endpunkt, der erst nach der JavaScript-Ausführung entsteht und dann ins DOM eingefügt wird
Ergebnisse aus den serverseitigen Zugriffslogs, dreimal wiederholt:
| Plugin-Konfiguration | Klasse A (HTML-Links) | Klasse B (JS-Datei-Literale) | Klasse C (zur Laufzeit im DOM) |
|---|---|---|---|
Ausgelieferter Standard — parse-(html|tika) | 4/4 (Recall 1,0) | 0/2 (Recall 0,0) | nicht erreicht |
Mit parse-js — parse-(html|tika|js) | 4/4 (Recall 1,0) | 2/2 (Recall 1,0) | nicht erreicht |
In allen drei Wiederholungen identisch. Deterministisch.
Der Sprung bei Klasse B wird leicht unterschätzt. Nutch fand beide in JavaScript eingebetteten Endpunkte ohne Browser-Ausführung, indem das parse-js-Plugin den JavaScript-Content per Regex durchsucht. Die Datei app.js selbst wurde in beiden Konfigurationen geholt — Nutch behandelt <script src> unabhängig vom Plugin als Outlink — der gesamte Unterschied besteht also darin, ob irgendetwas den Inhalt der Datei auf URL-ähnliche Strings prüft. Schaltet man das Plugin ein, erkennt es beide Literalformen.
Auf diesem Fixture erreichten Nutchs Standardmodus und katana im Standardmodus denselben Klasse-A-Bestand, während Nutch mit parse-js und katana mit -jc die Klassen A und B ohne Browser erreichten. Die genaue katana-Version und der vollständige Befehl sind in diesem Artikel nicht dokumentiert, daher ist das eher Kontext als ein strenger Produktvergleich.
Klasse C ist die ehrliche Obergrenze. Keine statische Plugin-Konfiguration erreichte sie, was zu erwarten ist: Einen Endpunkt zu rekonstruieren, der erst nach Skriptausführung existiert, setzt voraus, dass das Skript tatsächlich ausgeführt wird. Ich habe auch versucht, protocol-http durch protocol-htmlunit zu ersetzen, Nutchs reines Java-Protokoll mit JavaScript-Ausführung. Es lud und lief, ohne abzustürzen, aber im selben Vier-Runden-Harness schloss es nur eine Runde ab, holte nur die Seed-Seite und app.js, erreichte weder A noch B noch C, und Runde zwei meldete 0 records selected for fetching. Das ist ein unterkonfigurierter Test, kein Urteil über die Fähigkeit von HtmlUnit. Wichtiger ist die engere Aussage: Ein JavaScript-ausführendes Protokoll ist kein Drop-in-Wechsel, und Klasse C blieb in jeder getesteten Konfiguration unerreicht.
Crawl-Steuerung und Fehlverhalten
Tiefe ist kein Schalter. In Nutch gibt es kein --depth 3; Tiefe ist einfach die Anzahl der von Ihnen ausgeführten generate → fetch → parse → updatedb-Runden, weil Runde R genau die Frontier abruft, die in Runde R-1 entdeckt wurde. Meine Tiefenkette bestätigte das exakt:
| Ausgeführte Runden | Erreichte maximale Pfadtiefe |
|---|---|
| 2 | /depth/1 |
| 3 | /depth/2 |
| 4 | /depth/3 |
Sauber und mechanisch — aber eben eine Schleifenanzahl im Skript, kein Parameter.
Jetzt die Falle. Nutchs ausgelieferter Standardwert ist db.ignore.external.links=false, kombiniert mit einem großzügigen +.-URL-Filter — das heißt: Ein Standard-Crawl folgt Links auch außerhalb Ihrer Seed-Host-Domäne. Ich habe eine Seite mit einem in-scope-Pfad und einem Link zu einem anderen Host gesät, und der Crawl holte den fremden Host. Zwei unabhängige Signale stimmten überein: Die eigene crawldb von Nutch markierte ihn als db_fetched, und der Server des anderen Hosts zählte die Anfrage mit.
Im Scope zu bleiben ist eine bewusste Entscheidung, und beide Lösungen funktionieren nachweislich:
| Konfiguration | Externer Host in der crawldb | Server-Hit auf dem externen Host | Eingrenzung? |
|---|---|---|---|
db.ignore.external.links=false (Auslieferungsstandard) | db_fetched | +1 | Nein |
db.ignore.external.links=true | nicht vorhanden | 0 | Ja |
Host-Regel in regex-urlfilter.txt (+^http://127.0.0.1: dann -.) | nicht vorhanden | 0 | Ja |
Wenn Sie nur eine einzige Website crawlen, setzen Sie einen dieser Wege vor dem ersten echten Lauf. Ein methodischer Hinweis: Dieser Test ist empfindlich gegenüber Last auf dem lokalen Server, daher stammen diese drei Zeilen aus einem Lauf, in dem sonst nichts auf das Fixture zugriff. Das Verhalten selbst ist mechanisch klar und durch zwei unabhängige Signale abgesichert; die konkreten Zeilenwerte stammen aus einem sauberen Einzellauf, nicht aus einem Durchschnitt vieler Läufe.
Sitemaps sind ein separater Schritt. Höflichkeit ist aktiviert — ein normaler Crawl holte /robots.txt — aber die Sitemap selbst braucht einen eigenen Befehl:
| Vorgehen | Wurde /sitemap.xml angefragt? | Endpunkte, die nur in der Sitemap existierten |
|---|---|---|
| Ein normaler Crawl | nie angefragt | 0/2 |
bin/nutch sitemap, explizit gegen die crawldb ausgeführt | abgerufen | 2/2 Einträge injiziert, volle Recall |
katanas inline -kf-Known-Files-Modus, gleiches Fixture, auf einem IP-Host | nicht erfasst | 0/2 |
Ein anderes Modell als Crawler, die bekannte Dateien inline ziehen, und es kostet einen zusätzlichen Befehl — aber es erledigt die Aufgabe vollständig.
Zwei kleinere Verhaltensweisen hielten sich ebenfalls gut.
Fehlerbehandlung: Ein Crawl über eine Seite, die auf einen 500er und einen 404er verwies, lief alle Runden sauber durch, holte weiterhin alle vier Klasse-A-Seiten und verzeichnete beide Fehler getrennt:
| Von der Seite verlinkte fehlgeschlagene Antwort | Erfasster crawldb-Zustand |
|---|---|
| 500 | db_unfetched (für Retry geeignet) |
| 404 | db_gone |
Nichts geriet ins Rutschen.
Höflichkeit/Politeness: Bei einem Thread pro Queue entsprach der Abstand zwischen zwei Abrufen vom selben Host der Einstellung:
fetcher.server.delay | Medianabstand zwischen Abrufen vom selben Host |
|---|---|
| 1,0 Sekunde | 1,009 s (Minimum 1,006 s) |
| 0,0 | 0,002 s |
Der Regler tut exakt das, was er verspricht. Der ausgelieferte Standardwert liegt bei 5,0 Sekunden — konservativ und, erneut, wahrscheinlich richtig für ein Werkzeug, das fremde Server crawlt.
Die Batch-Strafe, in Sekunden
Jeder Nutch-Befehl startet einen frischen JVM-Prozess. Diese einzelne Tatsache dominiert das Timingprofil stärker als alles, was mit dem eigentlichen Abruf zu tun hat.
| Phase (pro Runde) | Median in Sekunden |
|---|---|
inject (einmalig) | 1,81 |
generate | 3,93 |
fetch | 2,82 |
parse | 1,78 |
updatedb | 1,81 |
| Eine vollständige Runde | 12,14 |
Die effektive Untergrenze pro Job — JVM-Start plus Hadoop-Initialisierung, gemessen als günstigste Phase mit trivialer Arbeit — liegt bei etwa 1,77 Sekunden. Multipliziert man das mit vier Befehlen pro Runde und addiert das anfängliche Inject, ergibt sich für den gesamten Crawl folgendes Bild:
| Tool | Crawl mit Tiefe 4 auf dem 12-Seiten-Fixture | Prozesse |
|---|---|---|
| Nutch | ungefähr 45 Sekunden (ich habe 45,8 s und 45,0 s über zwei Konfigurationen gemessen) | rund 17 JVM-Starts, von denen praktisch keiner echte Netzwerkarbeit leistet |
katana im Modus standard, gleiches Fixture | etwa 13 Sekunden | ein Prozess |
Diese Differenz hat nichts mit Fetch-Durchsatz zu tun; beide Werkzeuge rufen dieselbe kleine Seitenmenge ab. Es ist architektonisch bedingt. Nutch zahlt pro Phase einen festen Prozess-Preis, weil diese Phasen als MapReduce-Jobs designt sind. Bei einem kleinen lokalen Crawl dominiert das Setup. Dieser Fixkostenteil sollte bei längeren Jobs relativ kleiner werden, aber dieser Test hat nicht den Maßstab gemessen, bei dem sich Nutch und katana annähern oder ihr Verhältnis umkehren.
Vor- und Nachteile
Vorteile
- Deterministische statische Entdeckung: 4/4 bei HTML, 3/3 in der Tiefenkette, identisch über drei Wiederholungen.
parse-jserkennt JavaScript-Dateiliteral-Endpunkte (2/2) ohne Browser, sowohl in der Argument- als auch in der Zuweisungsform.- Zwei verifizierte Scope-Kontrollen, die einen Crawl vollständig eingrenzen (
db.ignore.external.linksund der Host-Filter inregex-urlfilter). - Sitemap-Einlesung per
bin/nutch sitemaperreichte volle 2/2-Recall für Endpunkte, die ein normaler Crawl komplett verfehlte. - Robust bei Fehlern: 500 und 404 werden mit unterschiedlichen crawldb-Zuständen behandelt, der Crawl läuft weiter.
- In diesem lokalen Lauf entsprach das beobachtete Intervall zwischen Abrufen desselben Hosts der konfigurierten 1,0-Sekunden-Verzögerung; der ausgelieferte Standard liegt bei 5,0 Sekunden.
- Apache-2.0, aktiv gepflegt, 78 Plugins und eine persistente crawldb, die den Zustand pro URL über mehrere Runden hinweg verfolgt.
- Läuft im Local Mode ohne Cluster, ohne HDFS und ohne notwendiges Solr.
Nachteile
- Läuft nicht auf JDK 24 oder neuer, weil dort der SecurityManager-Weg weggebrochen ist (den Fehler habe ich auf 26.0.1 gemessen) — das gebündelte Hadoop 3.4.2 liegt vor dem Upstream-Fix, und der Ausweichschalter existiert nicht mehr, daher ist ein festes LTS-JDK keine Vorliebe, sondern Pflicht.
- Entpackt ≈396 MB, 188 Bibliotheks-JARs, 78 Plugin-Verzeichnisse, 35 Konfigurationsdateien.
- Neuer JVM-Start pro Befehl bedeutet rund 1,77 s Fixkosten pro Phase; etwa 45 s für einen Crawl mit Tiefe 4 über 12 Seiten gegenüber rund 13 s bei einem Single-Binary-Crawler auf identischem Testgrund.
- Der ausgelieferte Standard folgt Links zu externen Hosts; im Scope zu bleiben ist eine explizite Entscheidung.
http.agent.nameist leer ausgeliefert, und der Fetcher verweigert den Lauf, bis Sie ihn setzen.- Kein Tiefenflag — die Tiefe ist eine Schleifenanzahl, die Sie selbst verwalten.
- Endpunkte, die erst zur Laufzeit im DOM entstehen, waren in jeder getesteten Konfiguration unerreichbar, und der Wechsel auf das JavaScript-ausführende Protokoll war kein Drop-in.
- Getestet habe ich den Local Mode auf einem einzelnen Host mit einem kleinen Fixture. Distributed/HDFS-Modus, Solr-Indexierung, hostdb, Resume und inkrementelle Re-Crawl-Planung waren in diesem Durchlauf nicht enthalten — bitte hier als ungetestet und nicht als bestätigt behandeln.
Für wen es sinnvoll ist — und wer lieber weiterzieht
Nutch spielt seine Stärken aus, wenn der Crawl selbst die eigentliche Hürde ist. Wenn Sie einen Suchindex bauen, einen breiten Crawl über viele Domains fahren, eine persistente URL-Datenbank mit per-URL-Zuständen und Retry-Semantik brauchen oder später über mehrere Maschinen skalieren wollen, ist das Infrastruktur, die genau diese Arbeit seit einer Zeit erledigt, bevor die meisten Alternativen überhaupt existierten. Das Plugin-System erlaubt es, Protokoll-, Parser-, Filter- und Scoring-Verhalten zu ändern, ohne etwas forken zu müssen. Die Höflichkeits-Defaults sind bewusst konservativ — ein Hinweis darauf, dass die Maintainer ernsthaft darüber nachgedacht haben, sich im Netz anständig zu verhalten.
Ziehen Sie weiter, wenn Sie aus ein paar Seiten strukturierte Daten brauchen. Nutch holt und parst sie dann zwar, übergibt Ihnen aber eine crawldb und Segmente und erwartet, dass Sie selbst einen Indexer mitbringen. Ziehen Sie weiter, wenn Ihre Ziele clientseitig gerenderte Single-Page-Apps sind — Klasse C blieb in allem, was ich ausgeführt habe, unerreicht. Ziehen Sie weiter, wenn Ihr Team keinen JVM-Stack betreibt, denn dann würden Sie eine Java-Toolchain, ein festes LTS-JDK und 396 MB JARs zu einem Stack hinzufügen, der davon bisher nichts hat. Und wenn die Aufgabe lautet: „eine Website einmal pro Woche vier Ebenen tief crawlen“, verbringen Sie mehr Zeit mit Rundenlogik und Konfigurationsdateien, als der Crawl verdient.
Für die meisten, die einen Scraper suchen, ist genau das die reale Situation. Das ist kein Vorwurf an Nutch — es ist ein Missverhältnis zwischen Werkzeug und Aufgabe. Wenn Sie ein Gefühl für das breitere Feld bekommen möchten, decken unser Überblick über Open-Source-Scraper und die besten Web-Scraping-GitHub-Projekte die leichtere Seite des Spektrums ausführlicher ab.
Alternativen, einschließlich der Einordnung unseres eigenen Stacks
Zuerst die faire Einordnung: Nutch ist kostenlos, Apache-lizenziert, selbst gehostet und gehört Ihnen für immer ohne Kosten pro Anfrage. Das ist ein echter Vorteil, und nichts weiter unten nimmt ihm das.
Verwandte Rezension: Browsertrix Crawler im Test.
Im Open-Source-Bereich hängt der Vergleich davon ab, worauf Sie optimieren. Wenn Sie ein Python-Framework mit Crawl-Kontrolle und einer Request-first-Philosophie wollen, ist Scrapy für viele Projekte das nähere Gegenstück; die Installationsgröße wurde in diesem Artikel nicht auf derselben Basis gemessen. Wenn Sie einen kompakten Go-Crawler ohne Browser suchen, ist Colly eine weitere Form, die man ansehen sollte. Wenn Ihr Problem eher darin besteht, Seiten in LLM-taugliche Inhalte zu verwandeln als URLs zu entdecken, ist Crawl4AI auf eine andere Ebene ausgerichtet.
Ein Managed Service wie Thunderbit verlagert Abruf, Rendering und Extraktion hinter eine API, während Nutch Crawl-Zustand und Infrastruktur unter Ihrer Kontrolle hält. Thunderbit wurde auf diesem Fixture nicht ausgeführt; das hier ist also ein Vergleich der Besitz- und Betriebsmodelle, keine Behauptung über gleiche Recall-Werte oder dynamische Seitenleistung.
Der Trade-off ist Kontrolle gegen Overhead, und er ist nicht subtil. Nutch gibt Ihnen volle Kontrolle, eine persistente crawldb, Cluster-Skalierung im Design und keine variablen Kosten — dafür aber eine JVM, ein festes LTS-JDK, 396 MB JARs, eine Schleifenlogik und Ihre eigene Indexierungsschicht. Eine Managed API liefert strukturierte Ausgabe schon beim ersten Aufruf und benötigt keine Infrastruktur — dafür aber Preise pro Aufruf und weniger Kontrolle über die Crawl-Frontier. Wenn Ihre Aufgabe lautet „50 Millionen Seiten indexieren“, ist Nutchs Modell richtig und eine API absurd. Wenn Ihre Aufgabe lautet „bis Donnerstag 200 Produktseiten in strukturierte Datensätze verwandeln“, gilt das Gegenteil.
Thunderbit für Web-Datenextraktion testen
Fazit
Apache Nutch ist eine Prüfung wert, wenn Sie einen laufenden Crawl über viele Domains betreiben und bereits JVM-Infrastruktur einsetzen. Auf diesem Fixture war die statische Entdeckung über Wiederholungen hinweg deterministisch, parse-js fand beide Literal-Endpunkte in JavaScript, Fehler blieben in der crawldb sichtbar, und die beobachteten Anfrageabstände entsprachen der konfigurierten Verzögerung.
Den Einstiegspreis sollten Sie realistisch ansetzen. Nutch 1.22 scheiterte hier auf JDK 26.0.1; OpenJDK 17.0.20 ist die tatsächlich in diesem Review verifizierte LTS-Konfiguration, Java 21 wurde nicht getestet. Danach setzen Sie http.agent.name, definieren den Scope ausdrücklich und berücksichtigen die beobachtete Fixuntergrenze von etwa 1,77 Sekunden pro Phase in diesem kleinen lokalen Lauf. Ob sich dieser Trade-off lohnt, hängt von Dauer, Breite und dem Bedarf an persistentem Zustand ab.
Thunderbit für Web-Datenextraktion testen Get Started Free
FAQs
Warum schlägt Apache Nutch mit „getSubject is not supported“ fehl?
Unter JDK 24 oder neuer hat JEP 486 Subject.getSubject() zu einer immer auslösenden Ausnahme gemacht, während das gebündelte Hadoop 3.4.2 die Methode noch aufruft. Deshalb stirbt der erste Hadoop-Job, bevor überhaupt eine Seite abgerufen wird, und der frühere Ausweg über -Djava.security.manager=allow startet die VM nicht mehr. Verwenden Sie die verifizierte Java-17-Konfiguration und setzen Sie NUTCH_JAVA_HOME; Java 21 könnte unterstützt werden, wurde in diesem Review aber nicht im vollständigen Zyklus getestet.
Welche Java-Version sollte ich für Nutch 1.22 verwenden?
Java 17 ist die sicherste Wahl — Nutchs eigene CI zielt darauf, und in meinen Tests mit OpenJDK 17.0.20 lief es sauber. Java 11 wird für 1.22 ebenfalls noch unterstützt, auch wenn das Projekt angekündigt hat, dass 1.23 Java 17 voraussetzen wird. Alles ab JDK 24 läuft nicht mehr. Eine keg-only Homebrew-Installation (brew install openjdk@17) plus NUTCH_JAVA_HOME lässt das System-Standard-JDK unangetastet.
Kann Nutch JavaScript-lastige Websites crawlen?
Teilweise, und der Unterschied ist wichtig. Mit aktiviertem parse-js-Plugin fand Nutch beide Endpunkte, die nur als String-Literale in einer verlinkten JavaScript-Datei existierten — 2/2, ganz ohne Browser. Mit dem Standard-Plugin-Set fand es keinen davon. Ein Endpunkt, der erst nach der Ausführung von JavaScript und einer DOM-Änderung auftaucht, blieb in jeder statischen Konfiguration, die ich getestet habe, unerreichbar, und der Wechsel auf das HtmlUnit-Protokoll war in meinem Lauf kein Drop-in. Für clientseitig gerenderte Apps sollten Sie mit einem JavaScript-ausführenden Protokoll plus echtem Konfigurationsaufwand oder mit einem anderen Tool planen.
Braucht Nutch Hadoop und Solr installiert?
Nein. Local Mode nutzt Hadoops in-process-LocalJobRunner — kein Cluster, kein HDFS-Daemon, kein YARN — und der komplette Zyklus inject → generate → fetch → parse → updatedb läuft auf einer Maschine, ohne dass sonst etwas installiert sein muss. Solr ist zwar oft das Ziel für die Indexierung, aber der Crawl selbst braucht es nicht. Allerdings sind Hadoop-JARs gebündelt (13 Stück, Version 3.4.2), und genau deshalb existiert das JDK-Kompatibilitätsproblem überhaupt.
Wie verhindere ich, dass Nutch andere Websites mitcrawl’t?
Setzen Sie das explizit, denn der Standard tut es nicht. Nutch 1.22 liefert db.ignore.external.links=false zusammen mit einem großzügigen URL-Filter aus, und in meinem Test folgte der Standard-Crawl einem Link zu einem anderen Host und holte ihn ab. Setzen Sie entweder db.ignore.external.links=true in nutch-site.xml, oder fügen Sie eine Host-Regel in conf/regex-urlfilter.txt ein (zum Beispiel +^https://example\.com/ gefolgt von -.). Beide Varianten begrenzten den Crawl im Test vollständig, verifiziert über Nutchs eigene crawldb und über das Request-Log des anderen Servers.


