Mozilla's Readability ist die eigenständige JavaScript-Portierung des Extraktionskerns hinter Firefox's Reader View. Als Apache-2.0-Paket @mozilla/readability veröffentlicht, zieht es Artikelinhalte direkt aus einem live DOM heraus. Unter Node braucht es dafür also eine DOM-Implementierung wie jsdom. Es lädt keine Seiten, führt kein JavaScript aus und macht auch keine Schema-Extraktion.
Die getestete Version ist npm latest in 0.6.0, veröffentlicht am 3. März 2025. Als ich am 27. Juli 2026 nachgeschaut habe, hatte das Repo 11.361 Sterne; zuletzt gepusht wurde am 9. Juli 2026, also liegt main inzwischen deutlich vor dem veröffentlichten Paket. Ich habe es unter jsdom 29.1.1 auf Node v22.22.3 auf macOS arm64 gegen 22 gelabelte HTML-Fixtures getestet, die ich extra dafür gebaut habe — und alle Angaben hier stammen genau aus diesem Setup. Praktisch ist es das am wenigsten anspruchsvolle Tool in dieser Kategorie: zwei Minuten Installation, keine Binärdateien, kein Browser-Cache, bei jedem Lauf identische Ausgabe. Spannend ist nicht die Bedienung, sondern dass sich die Fehler aus ein paar Konstanten im Quellcode ziemlich gut vorhersagen lassen — und eine davon entscheidet mehr, als die Doku vermuten lässt.
Über 22 kontrollierte synthetische Fixtures hinweg hat Readability alle 74 markierten Artikelblöcke wiedergefunden. Dieses begrenzte Ergebnis heißt aber nicht, dass es nie Text verliert: Der öffentliche Real-Page-Benchmark weist einen Recall von 0,982 aus, und bekannte Fehlerbilder ließen sich hier nicht reproduzieren. Der sichtbarste Fixture-Fehler war zusätzlicher Geschwisterinhalt, der über eine Link-Density-Grenze auf Source-Ebene von 0,25 mitgenommen wurde. Wie schwer das wirkt, hängt vom Testaufbau ab.
Was readability.js tatsächlich ist — und drei Dinge, die es nicht ist
Readability ist ein regelbasierter Scoring-Durchlauf über ein DOM. Es läuft die Kandidatenelemente durch, vergibt Content-Scores, gibt diese Scores an die Vorfahren weiter, wählt den höchstbewerteten Teilbaum aus und räumt anschließend alles weg, was nach Seitenelementen aussieht. Das ist die gesamte Artikel-Extraktionsstrategie — kein Modell, keine Trainingsdaten, keine site-spezifischen Regeln. Genau deshalb funktioniert es auch auf Seiten, die es noch nie gesehen hat — und genau deshalb sind die Fehler aus dem Quellcode vorhersagbar, was den Reiz ausmacht.
Drei Dinge ist es nicht, und genau daran stolpern viele:
- Kein Fetcher. Es nimmt ein
document, keine URL. Abruf, Retries, Anti-Bot und Header sind Ihr Thema. - Kein Renderer. Kein JavaScript wird ausgeführt. Readability sieht nur das, was im DOM steckt, das Sie ihm geben.
- Kein strukturierter Extraktor. Sie bekommen
title,byline,excerpt,content(HTML),textContent,length,siteName. Kein Schema, keine typisierten Zeilen, kein{name, price}.
Vier Konstanten erledigen den Großteil der Arbeit

Wer Readability.js in node_modules liest, versteht das Verhalten oft besser als über jede Doku-Seite. Vier Mechanismen erklären den Großteil dessen, was die Bibliothek tut:
- Content-Score pro gewertetem Absatz:
1 + (commaCount + 1) + min(floor(len / 100), 3). Absätze unter 25 Zeichen zählen gar nicht. Die Scores werden mit Dividern an Vorfahren weitergereicht — der Parent bekommt den vollen Score, der Großelternknoten die Hälfte und tiefere Vorfahrenlevel · 3. DEFAULT_CHAR_THRESHOLD = 500— die minimale Artikel-Länge für einen „erfolgreichen“ Parse. Darunter läuft ein zweiter Durchgang mit weniger harten Bereinigungsregeln.- Der
unlikelyCandidates-Regex — er matcht Klassen- und ID-Teile wiecomment,footer,menu,related,sidebar,social,sponsor. Entsprechende Knoten werden vor dem Scoring entfernt. - Die Sibling-Append-Regel in
grabArticle— nachdem der Top-Kandidat gewählt ist, werden seine Geschwister auf Aufnahme geprüft. Ein Geschwisterknoten kommt mit, wenn sein eigener Score die Schwelle knackt oder wennnodeLength > 80 && linkDensity < 0.25gilt oder wennnodeLength < 80 && nodeLength > 0 && linkDensity === 0 && es einen Punkt enthält.
Link Density ist Σ(linkText.length · coef) / textLength, wobei coef = 0,3 für reine #-HREFs und sonst 1 ist. Diese Regel erklärt die hier gemessenen Geschwister-Leaks; sie ist aber nicht der ganze Extraktionsalgorithmus.
Setup und die Abhängigkeit, die im Pitch gern verschwindet
npm install @mozilla/readability jsdom — und los geht’s. Zwei Minuten, keine Binärdateien, kein Post-Install-Download, kein Browser im Cache. Auf der Installationsachse ist das fast ideal.
„Keine Abhängigkeiten“ bezieht sich aber auf den Algorithmus, nicht auf die Laufzeit. Readability arbeitet auf einem lebenden document, und unter Node heißt das: Sie liefern die DOM-Implementierung selbst mit — hier also jsdom 29.1.1. jsdom ist nicht klein, und in den meisten Pipelines ist es der dominante Kostenfaktor im Loop, nicht die Extraktion. Das sollte man einplanen.
Ein weiterer Stolperstein, der mich einen erneuten Lauf gekostet hat: Readability.parse() verändert das übergebene DOM. Parsed man dasselbe jsdom-Dokument zweimal, sieht der zweite Aufruf ein Dokument, das der erste bereits auseinandergenommen hat. In meinem Harness baut jeder Parse ein frisches jsdom auf. Wenn Sie bei Seiten in einer Schleife dasselbe Document-Objekt wiederverwenden, um Zeit zu sparen, ist das der Bug, den Sie bald melden werden.
So habe ich getestet
Ich habe das nicht gegen Live-Newsseiten laufen lassen. Live-Seiten geben Ihnen zwar eine Zahl, aber ohne zu zeigen, warum sie so ausfällt — und bei einer Heuristik ist genau dieses „Warum“ der eigentliche Wert. Stattdessen habe ich 22 HTML-Fixtures mit 91 markierten Blöcken (74 Artikel, 17 Boilerplate) erzeugt, bei denen jedes Wort eines Blocks mit dem eindeutigen Sentinel-String dieses Blocks vorangestellt ist. Die Vokabulare der Blöcke überschneiden sich nicht; dadurch lässt sich jedes extrahierte Token eindeutig genau einem Block zuordnen, und „wiedergefunden“ oder „geleakt“ ist ein exakter Mengenvergleich statt eines weichen Matches.
Extraktion und Bewertung sind absichtlich getrennt. Der Node-Runner gibt nur den rohen extrahierten Text, die isProbablyReaderable-Booleans und gemessene Link-Dichten aus. Präzision und Recall werden erst danach aus diesem Rohtext gegen die Labels berechnet — durch ein separates Skript. Keine Metrikkonstante wird irgendwo im Harness von Hand eingetragen; nur so traue ich meinen eigenen Zahlen.
Anschließend habe ich die identischen Bytes an trafilatura 2.1.0 für einen Vergleich auf demselben Testbett gegeben. Jedes Fixture wurde drei Mal geparst; alle 22 lieferten pro Lauf byte-identischen Text.
Jede Phase lässt sich einzeln prüfen, statt der Zusammenfassungstabelle einfach zu glauben. tests/build_fixtures.mjs erzeugt das annotierte HTML und die Ground Truth; tests/run_readability.mjs protokolliert Extraktion und Predictor-Ausgabe; und tests/metrics.py berechnet die Werte im Nachgang. Die rohen Readability-Ausgaben, die berechneten Metriken und der Vergleich mit identischen Eingaben liegen unter artifacts/raw/. Diese Trennung ist wichtig, wenn ein Ergebnis verdächtig aussieht: Dann sieht man, ob der Parser unerwarteten Text zurückgegeben hat, das Label-Set falsch war oder der Scoring-Code falsch klassifiziert hat. Die Reproduktion mit diesem Paket prüft die hier gemachten Aussagen, ist aber weiterhin nur eine Harness-Prüfung — kein Beleg dafür, dass eine reale Seitenmischung dieselbe Fehlerverteilung hat.
Die Scope-Grenze ist real und entscheidend: Das hier sind synthetische Kontrollseiten, kein Real-World-Korpus. Die maßgeblichen Real-Page-Zahlen stammen aus dem öffentlichen article-extraction-benchmark, der readability_js 0.6.0 — exakt die hier getestete Version — mit Word-F1 0,947 ± 0,005 (Precision 0,914 ± 0,008, Recall 0,982 ± 0,003) über rund 181 echte Seiten bewertet. Ich zitiere das; ich habe es nicht reproduziert.
Hier werden aktuelle Benchmark-Zeilen verwendet; veraltete historische Zeilen wurden ausgeschlossen. Die kontrollierten Fixtures ergänzen das um eine Block-für-Block-Zerlegung, die zeigt, welche Inhaltsform welche Regel triggert, statt den öffentlichen Real-Page-Korpus zu ersetzen.
Recall war im synthetischen Fixture-Paket perfekt
74 von 74. Über alle 22 Fixtures hinweg hat Readability keinen einzigen markierten Artikelblock verloren — und auf den elf sauberen synthetischen Fixtures mit Artikel und Boilerplate lag der mikro-averagierte Token-Recall bei 1.000. Kein einziger Artikelsatz ging verloren.
Zwei Einschränkungen gehören dazu:
Das sind saubere, einspaltige synthetische Seiten. Reale Artikel sind tiefer verschachtelt, mischen Anzeigen mitten im Text und verlieren manchmal den Einstiegssatz durch ein Scoring-Artefakt — solche Fälle sind im Tracker dokumentiert (#437, #901 sowie Content-vor-einer-Tabelle in #922). Meine Fixtures haben keinen dieser Fälle ausgelöst, ich behaupte also nicht, sie seien behoben — ich sage nur, dass mein Test sie nicht erreicht hat. Auf echten Seiten liegt der Benchmark-Recall für diese Version bei 0,982, nicht bei 1,000.
Trotzdem ist die Richtung des Ergebnisses der wichtige Punkt. Das Problem von Readability ist nicht, dass es Ihren Artikel wegwirft. Das Problem ist, was es mitnimmt.
Die Präzisionszahl und warum sie drei Labels braucht

Eine Zahl lässt sich leicht zitieren und schwer verteidigen. Auf den elf gemischten Fixtures hat Readability 5 von 17 Boilerplate-Blöcken behalten — eine Leak-Rate von 0,294.
Das ist keine reale Leak-Rate. Drei verschiedene Setups messen drei verschiedene Dinge, und nur eines beschreibt normale Seiten:
| Was die Zahl misst | Ergebnis |
|---|---|
| Adversarial gewichtetes Fixture-Set — 6 der 11 gemischten Seiten wurden gezielt gebaut, um die Sibling-Regel auszutricksen | 5 von 17 Boilerplate-Blöcken behalten (0,294) |
Die eine realistische Seite — Inhalt innerhalb von <article>, umgeben von Nav, Ad-Banner, Sidebar, Kommentaren und Footer, plus einem neutral klassifizierten Promo-Block | 5 von 6 Chrome-Blöcken entfernt; 1 behalten |
| ~181 echte Seiten, öffentlicher Benchmark (nicht mein Lauf) | Precision 0,914, Recall 0,982, Word-F1 0,947 — readability_js 0.6.0 |
Lesen Sie die erste Zeile als Stresstest, nicht als Prognose. Readability leakt in freier Wildbahn nicht 29 % Boilerplate. Auf der realistischen Seite wurde alles, was eine Klasse trug, die der unlikelyCandidates-Regex erfasst — nav-menu, ad-banner, sidebar, comments, site-footer — sauber entfernt, alle fünf. Der einzige Überlebende war der Block, den ich gezielt so gebaut habe, dass er diesem Regex entgeht.
Das 0,25-Gate: Wo die Boilerplate-Entfernung aufhört
Die Sibling-Append-Regel ist im Quellcode dokumentiert. Wo sie genau kippt, war meines Wissens bisher nicht gemessen. Also habe ich einen Verlauf gebaut: ein neutral klassifiziertes <p class="teaser-block"> außerhalb von <article>, ein eindeutiger Artikel mit vier Absätzen, der sicher als Top-Kandidat gewinnt, und sonst nur variierende Länge und Link-Density des Promos. Die Dichten werden mit Readabilitys eigener Formel berechnet, also zur Laufzeit gemessen und nicht angenommen:
| Promo-Block | Länge des Inner Text | Über 80 Zeichen | Gemessene Link-Dichte | Ergebnis |
|---|---|---|---|---|
| Keine Links | 126 | ja | 0,000 | behalten |
| Ein kurzer Link | 126 | ja | 0,143 | behalten |
| Ein längerer Link | 126 | ja | 0,278 | entfernt |
| Hälfte des Textes verlinkt | 126 | ja | 0,476 | entfernt |
| Ein Satz, endet mit Punkt | 60 | nein | 0,000 | behalten |
| Gleicher Text, ohne Punkt | 59 | nein | 0,000 | entfernt |
Die Quellregel nutzt eine 0,25-Schwelle; die gemessenen Samples umklammerten sie, mit 0,143 behalten und 0,278 entfernt. Ein separater Zweig behielt einen 60-Zeichen-Satz mit Punkt und entfernte die 59-Zeichen-Version ohne Punkt. Der Artikel-Recall blieb in beiden Ästen 4/4, sodass diese Samples den Precision-Effekt isolieren.
Außerhalb eines Test-Harness heißt diese Regel im Kern: Langer, linkarmer, neutraler Fließtext neben dem Artikel ist wahrscheinlich Artikel. Das trifft auf vieles zu, was eben nicht der Artikel ist — ein als Absatz formulierter „Weiterlesen“-Hinweis, ein Newsletter-Pitch, ein redaktioneller Hinweis, ein gesponserter Teaser, den das Marketingteam in ganzen Sätzen geschrieben hat, während der Link aus Tracking-Gründen entfernt wurde.
In einem RAG-Index kann so ein Promo-Absatz als extrahierter Chunk landen und dazu führen, dass Retrieval oder Generierung ihn als Artikelinhalt behandelt. Die Quellregel macht diesen Fehler plausibel; dieser Review hat allerdings keine Ende-zu-Ende-Retrieval- oder Modell-Zitationsbewertung durchgeführt.
Für einen site-spezifischen harten Filter sollten Sie bekannte Source-DOM-Container vorfiltern, die Vorfahren-Kette des Quellknotens zum Vergleich vor der Serialisierung behalten oder anschließend sorgfältig validierte Textmuster-Filter anwenden. Das zurückgegebene HTML allein bewahrt eventuell nicht mehr die Information, ob ein Knoten ursprünglich außerhalb des primären Containers lag. Das Sibling-Gate lässt sich über die öffentlichen Optionen nicht einstellen.
Drei Annahmen, die die Fixtures widerlegt haben
Die Fixtures widerlegten drei Annahmen: dass charThreshold kurze Artikel ablehnt, dass semantische Tags nötig sind und dass kurzer Nicht-Prosa-Inhalt entfernt wird. Die folgenden Befunde sind der relevante Teil; eine Preregistrierungs-Aussage ist dafür nicht nötig.
charThreshold = 500 ist keine Klippe
Die verbreitete Annahme lautet, ein Artikel unter 500 Zeichen käme als null zurück. Tut er nicht. Ich habe die Body-Länge von 120 bis 1500 Zeichen gegen charThreshold-Werte von 200, 500 und 1000 getestet:
| Body-Länge | Parse bei jedem Threshold erfolgreich | Extrahierte Länge |
|---|---|---|
| 120 | ja | 161 |
| 300 | ja | 342 |
| 460 | ja | 509 |
| 520 | ja | 569 |
| 800 | ja | 841 |
| 1500 | ja | 1555 |
Flach. Gleiche extrahierte Länge bei allen drei Threshold-Einstellungen, und zwar bei jeder Body-Größe. Die Schwelle entscheidet also nicht über den Rückgabewert — sie bestimmt nur, ob der Grab mit entfernten Cleanup-Flags noch einmal läuft. Auf einer sauberen Seite gibt es nichts zu entfernen, also liefert der Siebtest so oder so denselben Inhalt. Die echte null-Grenze ist: „überhaupt kein extrahierbarer Text“.
Und genau daraus ergibt sich der eigentliche Fehler hier, und der ist unangenehmer als ein False Null. Ich habe eine fast leere Seite gefüttert — eine Navigationsleiste und eine Vier-Wort-Meldung. Sie kam erfolgreich zurück, und der zurückgegebene „Artikel“ enthielt die Navigation. Wenn es keinen echten Artikel gibt, liefert Readability Boilerplate als Artikel aus. Wer in großem Stil crawlt und ein nicht-null Ergebnis als „diese Seite hatte Inhalt“ interpretiert, liegt damit falsch.
Semantische Tags erledigen die Arbeit nicht
Ich hatte erwartet, dass der Recall sinkt, wenn man das Gerüst wegnimmt. Derselbe Artikeltext, zwei Hüllen: einmal mit <main><article><h1> und aussagekräftigen Klassennamen, einmal mit <div class="x1"> und Absätzen als nackte <div>s. Ergebnis: 4 von 4 Artikelblöcken in beiden Fällen wiedergefunden, in beiden Fällen kein Boilerplate-Leak. Wenn der Artikel der klar dichteste Textblock auf der Seite ist, findet ihn das Längen-und-Komma-Scoring auch ohne semantische Hilfe. „Readability braucht <article>-Tags“ ist Folklore.
Die ehrliche Grenze dieser Aussage: Meine Seite hatte genau einen offensichtlichen Inhaltsblock. Wo Semantik sinnvoll ins Gewicht fallen könnte, wäre eine Seite mit zwei konkurrierenden dichten Teilbäumen — das habe ich nicht getestet.
Nicht-Prosa bleibt vollständig erhalten
Die Regel „Absätze unter 25 Zeichen zählen nicht“ ließ mich Verluste bei Tabellen und Bildunterschriften erwarten. Wieder falsch — diese Regel betrifft das Scoring, nicht die Aufnahme. Wenn der Container gewinnt, kommt alles darin mit:
| Inhaltstyp im Artikel | Readability | trafilatura |
|---|---|---|
| Prosa-Absätze (×2) | behalten | behalten |
| Datenzellen einer Tabelle (×2) | behalten | behalten |
<pre>-Codeblock | behalten | behalten |
Einzeilige <p>-Texte unter 25 Zeichen (×2) | behalten | behalten |
<figcaption> | behalten | entfernt |
| Gesamt | 8/8 | 7/8 |
Das ist ein Bereich, in dem der härter aufräumende Cleaner verliert. Wenn Ihre Seiten Dokumentation, Tutorials oder überhaupt Inhalte mit Codeblöcken und Bildunterschriften enthalten, ist das „ganzen siegreichen Teilbaum behalten“-Verhalten von Readability eher ein Vorteil.
isProbablyReaderable sagt Nein, wenn parse() Ja sagt

Die README empfiehlt, isProbablyReaderable(doc) als günstigen Vorabtest vor dem vollständigen Parse zu verwenden. In meinen Tests hat dieser Gatekeeper drei verschiedene Seitentypen abgelehnt, die parse() danach problemlos verarbeitet hat:
| Seitentyp | Predictor-Urteil | parse() | Welcher Hebel hilft |
|---|---|---|---|
Inhalt nur in <li>-Elementen | false | erfolgreich | keiner — bei jedem minScore 1–80 und jedem minContentLength 40–200 bleibt es falsch |
| Zehn Absätze, jeweils unter 140 Zeichen | false | erfolgreich | minContentLength ≤ 100 (minScore bewirkt nichts) |
| Ein Absatz mit 408 Zeichen | false | erfolgreich | minScore ≤ 10 (Score ist ≈16,4) |
| Normale Artikelseite (Kontrolle) | true | erfolgreich | — |
Die drei Fehler haben drei verschiedene Ursachen, und nur zwei davon lassen sich einstellen. Der <li>-Fall ist strukturell: Der Predictor bewertet nur p-, pre- und article-Knoten (plus Eltern von div > br), also trifft eine Seite, deren Inhalt nur in Listenelementen lebt, auf nichts, bekommt Score null, und keine Threshold-Justierung rettet sie — eine Form, die bereits in Issue #662 beschrieben wurde. Der Fall mit vielen kurzen Absätzen ist ein minContentLength-Gate: Jeder Absatz wird vor dem Scoring übersprungen, sodass zehn substanzielle Absätze zusammen trotzdem nichts ergeben; ein niedrigerer Wert behebt das, minScore nicht. Der Ein-Absatz-Fall ist reine Mathematik: Der Score ist sqrt(408 − 140) ≈ 16,4 und liegt damit unter dem Standard-minScore von 20 — ein einzelner Absatz braucht also 540 Zeichen (140 + 20²), um allein durchzukommen.
Die README warnt durchaus, dass der Predictor False Negatives erzeugt. Praktisch würde ich ergänzen: Verlassen Sie sich nicht nur auf ihn. Wenn eine Seite wichtig ist, parsen Sie sie und prüfen Sie die Ergebnislänge. Der Parse ist gemessen an dem jsdom-Aufbau, den Sie ohnehin schon bezahlen, nicht sonderlich teuer.
Gleiche Bytes, zwei Extraktoren
trafilatura 2.1.0 auf exakt denselben Fixtures laufen zu lassen, ist aussagekräftiger als zwei Zahlen aus zwei verschiedenen Testbetten nebeneinanderzustellen — die Eingabe ist schließlich Byte für Byte identisch:
| Metrik (11 gemischte Fixtures) | @mozilla/readability | trafilatura |
|---|---|---|
| Artikelblock-Recall | 1.000 | 1.000 |
| Behaltene Boilerplate-Blöcke | 5/17 (0,294) | 1/17 (0,059) |
| Token-F1 (micro) | 0.948 | 0.969 |
| Recall für Nicht-Prosa | 8/8 | 7/8 |
| Sehr kurzer Artikel (120 Zeichen), Token-F1 | 0.800 | 0.571 |
Keiner der beiden dominiert diese Fixtures. Trafilatura behielt weniger Geschwisterblöcke, während Readability mehr kurze und Nicht-Prosa-Inhalte stehen ließ. Die absolute Token-Precision beider Tools wird durch unlabelte Überschriftstexte gedrückt; die Block-Leak-Zahl ist deshalb das klarere direkte Signal. Der öffentliche Real-Page-Benchmark ordnet ihre Word-F1 zufällig ähnlich ein, aber Korpora und Metriken sind verschieden — das ist keine Validierung über Testbetten hinweg.
Robustheit, kurz: Ich habe eine absichtlich fehlerhafte Variante der kanonischen Seite ausgeführt (nicht geschlossene <p>, falsch verschachtelte <b>/<i>, ein einsames </div>) und bekam 3/3 Recall bei null Leaks, also dasselbe wie bei der sauber formatierten Version. Dafür gebührt der Credit dem jsdom-HTML5-Tree-Builder, der das Chaos repariert, bevor Readability es überhaupt sieht. Kein Fixture ließ den Parser abstürzen.
Pro und Contra
Vorteile
- Der Artikel-Recall ist die starke Seite: 74/74 markierte Blöcke in 22 synthetischen Fixtures wiedergefunden, Token-Recall 1.000 im gemischten Set.
- Regex-basierte Seitenelemente werden zuverlässig entfernt — Nav, Ad-Banner, Sidebar, Kommentare und Footer verschwinden auf der realistischen Seite komplett (5 von 6).
- Nicht-Prosa bleibt vollständig erhalten: Tabellen,
<pre>-Code, Bildunterschriften und Zeilen unter 25 Zeichen überleben alle (8/8), während trafilatura eine Bildunterschrift entfernt. - Keine Abhängigkeit von semantischem Markup — ein neutralisiertes
<div>-Artikelgerüst scorete identisch zur<article>/<main>-Variante. - Kurze Artikel werden nicht fälschlich verworfen: Sauberer Inhalt wurde bis hinunter zu 120 Zeichen wiedergefunden, identisch über
charThreshold200/500/1000. - Voll deterministisch: Alle 22 Fixtures lieferten über drei Läufe identischen Text.
- Zwei Minuten Installation, Apache-2.0, und die npm-Version ist die getestete Version (0.6.0) — also nichts daran ist veraltet.
Nachteile
- Das Sibling-Append-Gate ist ausnutzbar: Langer, linkarmer, neutral klassifizierter Promo-Text ist vom Artikeltext nicht zu unterscheiden und wird bei
linkDensity < 0.25mitgenommen. - Auf inhaltsarmen Seiten liefert es Boilerplate als Artikel statt
nullzurück — das Fast-Leer-Fixture kam mit der Navigation als Body zurück. isProbablyReaderableproduziert False Negatives bei drei separaten Seitentypen, von denen sich einer durch Tuning nicht beheben lässt.- Unter Laufzeitbedingungen braucht es ein vollständiges DOM — die „keine Abhängigkeiten“-Formulierung verschleiert den jsdom-Preis, der den Loop dominiert.
parse()verändert das Eingabedokument, daher muss das DOM pro Seite neu aufgebaut werden.- Kein Fetching, kein JavaScript-Rendering, keine strukturierte Ausgabe. Das ist ein Schritt in einer Pipeline, nicht die Pipeline.
- Die im Tracker gemeldeten Real-Page-Misses (Einstiegsabsatz und Verluste vor Tabellen) traten auf meinen Fixtures nicht auf; ich kann also nicht sagen, ob sie selten sind oder meine Seiten sie einfach nie erreicht haben.
Wer es nutzen sollte — und wer nicht
Greifen Sie zu Readability, wenn Sie HTML bereits in der Hand haben und den Artikel daraus in reinem JavaScript ziehen wollen, in einem Node-Service, wo eine zusätzliche Python-Abhängigkeit unpraktisch wäre. (Ich habe keine saubere Zeitverteilung erhoben, daher mache ich keine Geschwindigkeitsbehauptung außer: „Der jsdom-Aufbau dominiert den Loop, nicht die Extraktion.“) Reader-Mode-Funktionen, Offline-Archivierung von Artikeln, Newsletter, „Clean View“-Buttons, Browser-Extensions, Dokumentations-Pipelines mit Codeblöcken und Bildunterschriften — genau dort spielt es seine Stärke aus, und die Recall-Werte sprechen dafür. Außerdem lässt sich sein Verhalten direkt aus dem Quellcode nachvollziehen, was mehr wert ist, als es klingt, wenn man einem Kollegen erklären muss, warum genau dieser eine Block durchkam.
Lassen Sie es sein, wenn Sie bei der Boilerplate-Entfernung Präzision brauchen, und erst recht, wenn Sie einen LLM-Index füttern, in dem ein einzelner Promo-Absatz später als abrufbarer Chunk landet. Lassen Sie es sein, wenn Ihre Seiten Inhalte clientseitig rendern, denn Readability sieht nur das DOM, das Sie ihm geben, und führt kein JavaScript aus. Lassen Sie es sein, wenn Sie {title, price, sku} statt Fließtext brauchen — keine Einstellung verwandelt einen Content-Extraktor in einen schema-getriebenen Extraktor. Und wenn Sie Seiten verarbeiten, bei denen die Frage „Gab es hier überhaupt einen Artikel?“ echt ist, dann vertrauen Sie einem nicht-null Rückgabewert nicht als Antwort.
Alternativen und wo der Thunderbit-Stack passt
Das hier ist kein Seitenhieb auf eine freie Apache-2.0-Bibliothek, die von Mozilla gepflegt wird — Readability ist Infrastruktur, es läuft seit Jahren in Firefox, und für Reader-Mode-Extraktion ist es aus gutem Grund die Referenzimplementierung. Wer das größere Feld sehen will, findet einen laufenden Vergleich in der Open-Source-Scrapers-Übersicht und eine breitere Einordnung in den besten Web-Scraping-Tools.
Für dieselben Fixtures über alle sechs Extraktoren hinweg siehe den Vergleich von sechs Extraktionsbibliotheken.
Hinweis der Redaktion: Thunderbit ist unsere verwaltete Option für Rendering und Extraktion direkt von einer URL. Es wurde auf diesen Fixtures nicht ausgeführt, daher ist keine direkte Qualitätsgleichheit impliziert. Die relevante Frage ist, ob Sie bereits ein DOM haben und lokale Artikel-Extraktion wollen — oder ob Sie Abruf, Rendering und strukturierte Ausgabe als Service nutzen möchten. Self-Hosting spart zwar Nutzungsgebühren eines Vendors, bringt aber weiterhin Infrastruktur- und Wartungskosten mit sich.
Der ehrliche Trade-off: Readability ist kostenlos, transparent und gehört Ihnen — Sie können genau die Regel lesen, die über Ihr Ergebnis entschieden hat, was Ihnen bei einer verwalteten API nicht gegeben ist. Ein verwalteter Stack kostet Geld und verbirgt den Mechanismus, übernimmt aber die Fetch-Render-Structure-Schritte, die Sie sonst selbst zusammenbauen müssten. Wenn Sie das KI-gestützte Ende dieses Spektrums interessiert: Ich habe an anderer Stelle über Scraping beliebiger Websites mit KI und über AI-Crawler geschrieben. Wählen Sie danach, welche Stufen Sie tatsächlich selbst besitzen wollen.
Thunderbit für Webdaten-Extraktion testen
Fazit
Readability ist eine gute Wahl, wenn Sie bereits ein DOM haben, mit JavaScript/Node arbeiten und gelegentlich etwas zusätzlichen Geschwisterinhalt lieber akzeptieren als aggressiv Inhalte wegzulassen. In diesem Fixture-Paket hat es alle 74 markierten Artikelblöcke wiedergefunden und Tabellen, Code und Bildunterschriften behalten. Dieses Ergebnis gilt aber nur für synthetische einspaltige Seiten; der öffentliche Real-Page-Recall liegt bei 0,982, bekannte Verluste an Einstiegen und vor Tabellen ließen sich nicht reproduzieren, und inhaltsarme Seiten können Boilerplate als Artikel zurückgeben.
Wichtig ist nur, die Schwäche korrekt einzuordnen. Die Haupt-Fehlerfläche in diesen Fixtures ist Präzision, und sie liegt an einer konkreten, dokumentierten Zeile im Quellcode: Ein Geschwisterknoten mit mehr als 80 Zeichen und einer Link-Density unter 0,25 wird dem Artikel angehängt, egal ob er wirklich dazugehört oder nicht. Ich habe diesen Umschaltpunkt bei 0,143 versus 0,278 auf identischem Text beobachtet. Der Real-Page-Benchmark weist eine Precision von 0,914 und einen Recall von 0,982 aus. Wenn Sie extrahierten Text in einen Index einspeisen, den ein Modell später zitieren soll, prüfen Sie sowohl erhaltene Boilerplate als auch verlorenen Body-Text, statt einfach anzunehmen, dass keine der beiden Fehlerklassen vorkommt.
Thunderbit für Webdaten-Extraktion testen Get Started Free
FAQs
Entfernt Mozilla Readability wirklich die ganze Boilerplate?
Nein, und die Zahl hängt stark davon ab, was Sie messen. Im öffentlichen Real-Page-Benchmark erreicht readability_js 0.6.0 eine Precision von 0,914 — also ist rund 8,6 % dessen, was zurückkommt, kein Body-Content. Auf meiner realistischen Testseite hat es 5 von 6 Chrome-Blöcken entfernt (Nav, Ad, Sidebar, Kommentare, Footer alle weg) und nur einen neutral klassifizierten Promo-Absatz behalten. In einem Fixture-Set, das ich absichtlich mit Blöcken gegen die Heuristik gewichtet habe, behielt es 5 von 17 — dieser letzte Wert ist ein Stresstest, keine reale Quote.
Brauche ich jsdom, um readability.js unter Node zu verwenden?
Ja, oder eine andere DOM-Implementierung. Readability ist reines JavaScript, arbeitet aber auf einem lebenden document-Objekt, also liefern Sie unter Node das DOM selbst mit — in meinem Setup jsdom 29.1.1. Die Beschreibung „keine Abhängigkeiten“ bezieht sich auf den Algorithmus, nicht auf die Laufzeit. Beachten Sie außerdem, dass parse() das übergebene Dokument verändert; bauen Sie also für jede Seite ein frisches DOM auf, statt eines wiederzuverwenden.
Was macht die Option charThreshold eigentlich?
Nicht das, was die meisten annehmen. Sie sorgt nicht dafür, dass kurze Artikel null zurückgeben — ich habe saubere Artikel bis hinunter zu 120 Zeichen wiederhergestellt, mit identischer extrahierter Länge bei charThreshold 200, 500 und 1000. Die Schwelle entscheidet nur, ob der Parser den Grab mit entfernten Cleanup-Flags erneut ausführt; auf einer sauberen Seite gibt es nichts zu entfernen, also bleibt die Ausgabe gleich. Das echte null-Szenario ist eine Seite ohne extrahierbaren Text — und selbst eine reine Navigationsseite kam nicht als null zurück, sondern lieferte die Navigation als Artikel.
Sollte ich vor parse() isProbablyReaderable aufrufen?
Verwenden Sie es als Hinweis, nicht als Gate. Es lieferte false auf drei Seitentypen, auf denen parse() danach erfolgreich war: Inhalt nur in <li>-Elementen, zehn Absätze unter 140 Zeichen und ein einzelner Absatz mit 408 Zeichen. Der <li>-Fall lässt sich nicht per Tuning reparieren, weil der Predictor nur p-, pre- und article-Knoten bewertet; der Fall mit vielen kurzen Absätzen braucht ein niedrigeres minContentLength; der Ein-Absatz-Fall ein niedrigeres minScore, weil ein einzelner Absatz 540 Zeichen erreichen muss, um die Default-Schwelle zu knacken. Wenn eine Seite wichtig ist, parsen Sie sie und prüfen Sie das Ergebnis.
Readability oder trafilatura für Artikel-Extraktion?
Auf identischen Fixture-Bytes hielt trafilatura weniger Boilerplate fest (1/17 Blöcke gegenüber 5/17), während Readability mehr kurze Inhalte wiederfand und einen <figcaption> behielt, den trafilatura entfernte. Entscheiden Sie nach Fehlertoleranz und Laufzeit. Der öffentliche Benchmark ist ein separater Kontext, keine Validierung dieses Fixture-Ergebnisses.


