Le seuil de jusText face à un corpus de courts paragraphes

Dernière mise à jour le August 17, 2026
Le seuil de jusText face à un corpus de courts paragraphes
Résumé IA
Dans un document dont le paragraphe le plus long mesure 151 caractères, jusText, avec ses réglages par défaut, renvoie zéro caractère. Pas une extraction partielle — une chaîne vide. En abaissant un seul seuil de sorte qu’un seul paragraphe le franchisse, le même document renvoie 832 caractères. En l’abaissant encore, on reste à 832. Sur ce jeu de test, le comportement ressemble à une falaise plutôt qu’à une pente. Le fait que la valeur par défaut soit du bon ou du mauvais côté dépend de la longueur des paragraphes et de la répartition du boilerplate dans votre corpus cible. Évaluez jusText pour des corpus multilingues, car la surface de stoplists spécifiques à chaque langue est explicite et large.

Dans un document dont le paragraphe le plus long mesure 151 caractères, jusText, avec ses réglages par défaut, renvoie zéro caractère. Pas une extraction partielle — une chaîne vide. Abaisse un seul seuil de façon à ce qu’un seul paragraphe le dépasse, et le même document renvoie 832 caractères. En l’abaissant encore, on reste à 832.

Sur ce jeu de test, le comportement ressemble à une falaise plutôt qu’à une pente douce. Le fait que la valeur par défaut tombe du bon ou du mauvais côté dépend de la longueur des paragraphes et de la répartition du boilerplate dans ton corpus cible.

Ce qu’est jusText, et pourquoi la densité lexicale compte

Par rapport aux autres extracteurs de ce jeu de test, jusText s’appuie de façon assez particulière sur la densité de stopwords propres à la langue. Un bloc qui contient beaucoup de mots-outils — le, et, de, était — a plus de chances d’être du texte rédigé. Ce signal lexical n’est pas le seul critère du classifieur : la longueur des paragraphes, la densité de liens, les frontières de blocs issues du HTML, la distance aux titres, les classes voisines et un second passage contextuel jouent aussi sur le résultat.

Référence officielle : dépôt officiel de jusText.

System diagram: Two-Pass Paragraph Classification

Comme le classifieur utilise une liste d’arrêt par langue, jusText en embarque 100. Cette surface lexicale explicite et spécifique à la langue est sa différence la plus nette dans cette comparaison, même si la qualité multilingue n’a pas été testée.

Version testée : 3.0.2, BSD 2-Clause, 822 étoiles GitHub. Python 3.14.2.

La falaise, mesurée

Measured results chart: Characters returned as length_high changes

Le classifieur de jusText fonctionne en deux passes. Une première passe, sans contexte, attribue à chaque paragraphe l’une des classes good, bad, short ou neargood. Puis une seconde passe, sensible au contexte, promeut neargood en good — mais uniquement lorsqu’il se trouve à côté d’un bloc déjà classé good. Et un paragraphe n’obtient la classe good tout seul que s’il dépasse length_high, qui vaut par défaut 200 caractères.

Dans un document où rien ne dépasse 200, rien ne sert de point d’ancrage à cette promotion, et tous les blocs neargood finissent considérés comme du boilerplate. La page revient donc vide.

J’ai fait varier ce seuil sur un jeu de test dont le paragraphe le plus long mesure 151 caractères :

length_highParagraphes classés goodCaractères renvoyés
200 (par défaut)00
1508832
1208832
1008832
808832

justext-length-threshold.json.

Sur ce jeu de seuil, le simple fait qu’un paragraphe franchisse la limite a fait basculer les huit paragraphes visés vers la sortie, et baisser davantage le seuil n’a rien ajouté. La trace correspond à une passe contextuelle qui promeut les voisins neargood admissibles autour d’un bloc good existant ; elle n’implique pas qu’un voisin soit promu sans condition sur n’importe quelle page.

Avant d’attribuer ce basculement à length_high, j’ai fait varier length_low sur quatre valeurs croisant max_link_density sur deux : huit combinaisons, toutes à zéro. Changer l’un ou l’autre de ces réglages n’a pas permis de récupérer du contenu sur ce jeu de test.

Sur l’ensemble des 22 jeux de test, le schéma se confirme : 2 sur 22 ont produit du contenu avec length_high=200, 9 sur 22 à 150, 15 sur 22 à 120.

Deux précisions sur ce que cela ne veut pas dire. Cela ne veut pas dire que jusText extrait mal — sur une vraie page en langue naturelle, avec les réglages par défaut, il a renvoyé 1 190 caractères de texte d’article propre, parce qu’un vrai paragraphe de presse dépasse 200 caractères dès le premier passage. Et cela ne veut pas dire que la valeur par défaut est mauvaise ; cela veut dire qu’elle suppose des paragraphes longs, et qu’il faut vérifier si ton corpus en contient avant de lancer quoi que ce soit.

L’autre face de ce jeu de test : davantage de fuite avec les réglages par défaut

Sur ce jeu de test étiqueté, avec les réglages par défaut, jusText affiche la fuite de boilerplate la plus élevée mesurée.

BibliothèqueRappel d’article (22/22)Fuite de boilerplatePrécision des tokens de contenuTokens contaminant
Readability1.00000.23530.910935
trafilatura0.98650.05880.94114
newspaper4k0.98650.00000.94520
resiliparse0.90540.05880.93817
jusText0.83780.47060.876074
goose30.82430.00001.00000

sixway-scores.json. Un seul jeu, un seul évaluateur, et chaque unité est marquée par une sentinelle unique afin que la récupération se fasse par appartenance exacte à une sous-chaîne.

System diagram: Leakage and Silence Share a Boundary

47 % de fuite de boilerplate et 74 tokens contaminés — soit le double du taux de fuite de Readability et plus du double de sa contamination sur ce jeu synthétique étiqueté. Il s’agit d’un résultat diagnostique avec les réglages par défaut, pas d’un classement stable à l’échelle de tout le produit.

La fuite observée est cohérente avec la même passe contextuelle que celle en jeu dans la falaise. Des blocs voisins de type prose, s’ils sont admissibles, peuvent être promus lorsque les classes et les règles de distance le permettent ; un promo ou un commentaire ressemblant à de la prose placé à côté du texte principal peut donc franchir la frontière. Le résultat du jeu de test montre quels blocs étiquetés ont fui, tandis que le mécanisme simplifié reste conditionné aux règles contextuelles exactes de jusText.

Son rappel est de 0.8378, troisième en partant du bas, et toute la perte vient du seuil : il n’a rien récupéré sur un article unique de 129 caractères, rien sur une page de dix courts paragraphes, et rien sur le document quasi vide.

L’inventaire des listes d’arrêt par langue

Quatre-vingt-dix-neuf autres listes d’arrêt.

justext.get_stoplists() renvoie 100 langues. Le classifieur est conçu dès le départ comme dépendant de la langue, et non comme une heuristique anglaise traduite ; changer de langue ne demande qu’un seul argument :

import justext
paragraphs = justext.justext(html, justext.get_stoplist("Czech"))
text = "\n".join(p.text for p in paragraphs if not p.is_boilerplate)

trafilatura et goose3 exposent eux aussi des comportements liés à la langue, mais cette revue n’a évalué aucun extracteur sur des vérités terrain non anglaises. Les 100 stoplists intégrées de jusText en font un candidat évident pour une évaluation multilingue ; leur simple inventaire ne prouve ni la qualité d’extraction dans ces langues, ni que les concurrents les couvrent moins efficacement.

L’API tient en deux fonctions et neuf constantes ajustables — length_low 70, length_high 200, stopwords_low 0.30, stopwords_high 0.32, max_link_density 0.20, max_heading_distance 200, plus la gestion de l’encodage. C’est simple, lisible, et tout est documenté dans la signature.

Installation et vitesse

pip install justext installe 3 paquets — le minimum de la comparaison — et 22,4 MiB, en moins de deux secondes.

Référence officielle : jusText sur PyPI.

BibliothèquePaquetssite-packagesImport à froidExtraction p50
resiliparse521.0 MiB0.015 s0.06 ms
jusText322.4 MiB0.777 s0.56 ms
goose31644.3 MiB2.181 s1.85 ms
newspaper4k2247.5 MiB2.812 s2.69 ms
trafilatura1769.9 MiB1.584 s0.51 ms

install-and-import.json. Chaque bibliothèque dans son propre environnement virtuel vide.

Trois paquets et 22,4 MiB représentent une dépendance légère dans cette comparaison, et une extraction médiane à 0,56 ms reste proche des 0,51 ms de trafilatura dans ce banc d’essai. La mesure d’environnement ne détaille pas la taille du paquet par type de fichier, donc elle ne permet pas de dire quelle part de l’empreinte disque provient des stoplists.

La question de la maintenance, traitée avec prudence

Les mesures de maintenance datées utilisées ici sont la dernière contribution au dépôt et la dernière version PyPI, toutes deux au 2025-02-25. Cela remonte à dix-sept mois avant les tests. Huit versions au total, 91 forks, 9 issues ouvertes, et le dépôt n’est pas archivé.

Les classifieurs PyPI indiquent une prise en charge de Python jusqu’à la 3.9. Je l’ai exécuté sur 3.14.2 et il s’est installé, a importé en 0,777 s et a extrait sur 19 jeux de test sur 22 sans lever la moindre exception.

Les métadonnées ont donc cinq versions de Python de retard sur la réalité, et la réalité, c’est que ça fonctionne. C’est une distinction utile : un dépôt silencieux dit quelque chose sur le support, pas automatiquement sur le fonctionnement. Pour une bibliothèque dont tout l’algorithme repose sur une méthode publiée en 2011 plus des listes de mots, l’état « terminé » est plausible — il ne reste pas grand-chose à modifier, et les stoplists ne vieillissent pas comme une parade anti-bot contournée en urgence.

Ce qu’un dépôt calme signifie quand même : si tu tombes sur un bug, c’est à toi de le corriger ou de forker. Il faut mettre cela en balance avec seulement neuf issues ouvertes, ce qui n’évoque pas une bibliothèque noyée sous des problèmes non traités.

Mémoire, et effet du HTML cassé

Deux questions opérationnelles sont mesurées séparément ici.

Le contexte plus large du test de stress se trouve dans la comparaison de dix bibliothèques sur la mémoire et le HTML malformé.

Mémoire résidente maximale, via /usr/bin/time -l, avec un processus neuf par cellule — le plancher d’import correspond à ce que la bibliothèque consomme une fois chargée et inactive, les pics incluent le document.

BibliothèqueExécutionPlancher d’importPic à 226 KBPic à 10 MB
html2textpython3.1418.719.971.2
pyquerypython3.1430.333.9172.5
resiliparsepython3.1420.525.1225.1
markdownifypython3.1423.928.9278.5
goose3python3.1444.152.4398.5
cheerionode2266.876.5398.5
justextpython3.1430.336.6431.2
newspaper4kpython3.1452.661.8668.5
trafilaturapython3.1452.564.8927.1
turndownnode2247.868.42947.1

memory-results.json. Les bases de comparaison Python et Node ne sont pas directement comparables ; l’interpréteur est présent dans les deux cas.

Le plancher de jusText est de 30,3 MiB et son pic sur ce fichier de 10 MB atteint 431,2 MiB, soit environ 14,2 fois le plancher d’import et 43,1 fois la taille d’entrée en RSS absolu. Un seul jeu de test et un seul processus ne suffisent pas à établir une courbe de montée en charge générale ; les bases Python et Node restent elles aussi non comparables.

HTML cassé. Douze documents, chacun brisant exactement une chose — balises non fermées, éléments en ligne mal imbriqués, attributs non quotés contenant des espaces, fermetures parasites, absence totale de <html>, attributs dupliqués, document tronqué au milieu d’une balise, entités invalides, <script> non fermé, déclaration d’encodage mensongère, commentaire contenant du balisage, et 600 niveaux d’imbrication — plus deux contrôles bien formés de taille équivalente, car « il n’a rien renvoyé » ne dit quelque chose sur le caractère mal formé que si la bibliothèque reste aussi silencieuse sur un document propre de même taille.

justext a levé une exception sur 0 des 14 cas et n’a rien renvoyé sur 13, récupérant 0/33 sentinelles scorées à travers les jeux cassés (malformed-results.json). Le contrôle court bien formé a lui aussi renvoyé 0 caractère ; le contrôle long bien formé est le seul résultat non vide, avec 1 333 caractères. Les douze jeux malformés sont tous restés vides, et le cas du <script> non fermé est exclu du scoring de survie des sentinelles. Cette série montre donc une tolérance du parseur — aucune exception — mais ne permet pas d’attribuer le silence au HTML mal formé plutôt qu’au seuil de taille déjà mesuré.

Avantages et inconvénients

En sa faveur. 100 stoplists par langue, et un classifieur réellement construit autour d’elles plutôt que simplement traduit en elles. Le plus petit nombre de dépendances de la comparaison, avec 3 paquets. 0,56 ms d’extraction médiane. Neuf constantes de réglage documentées et lisibles. BSD 2-Clause. Fonctionne proprement sur Python 3.14 malgré des classifieurs qui s’arrêtent à 3.9.

Contre lui. La plus forte fuite de boilerplate dans ce test synthétique avec les réglages par défaut : 47 %, et 74 tokens contaminés. Le jeu de courts paragraphes renvoyait une chaîne vide lorsqu’aucun paragraphe ne franchissait length_high. Le rappel était de 0,8378 sur cet ensemble. La dernière contribution au dépôt et la dernière publication remontent à dix-sept mois avant les tests, donc la question de la maintenance mérite d’être considérée.

Qui devrait l’utiliser, et qui ne devrait pas

Évalue jusText pour des corpus multilingues, car la surface de stoplists spécifiques à chaque langue est explicite et large. Ce test a bien inventorié 100 stoplists, mais n’a pas mesuré la qualité multilingue. C’est aussi un bon candidat quand une dépendance en trois paquets et des seuils explicites conviennent au déploiement.

Évite-le si tu alimente un modèle token par token, car 74 tokens contaminés contre zéro pour goose3 et newspaper4k ont un coût direct. Évite-le pour des pages à courts paragraphes — présentations produit, listings, changelogs, entrées de FAQ — sauf si tu as volontairement ajusté length_high. Et évite-le si tu as besoin d’une dépendance activement maintenue pour des raisons de conformité ou d’achat, ce qui est une contrainte réelle même quand le code fonctionne.

Si tu l’utilises, ajuste length_high sur un échantillon de validation étiqueté au lieu de recopier la valeur par défaut ou une règle au percentile. Fais varier des seuils plausibles et mesure à la fois le rappel des articles et la précision sur le boilerplate ; abaisser la barre peut récupérer des textes courts, mais aussi promouvoir des blocs voisins indésirables.

Où une API managée trouve sa place

jusText prend du HTML que tu as déjà, comme tous les outils de cette comparaison. Aucune de ces bibliothèques ne récupère une page, ne rend du JavaScript ni ne gère une couche anti-bot.

Pour les mêmes jeux de test sur les six extracteurs, voir la comparaison d’extraction en six bibliothèques.

Un service hébergé de récupération/rendu/extraction, y compris notre propre Thunderbit, couvre un autre périmètre de responsabilité. Thunderbit n’a pas été benchmarké ici. La distinction est celle d’une classification de prose à partir de HTML fourni, versus un service qui récupère et traite une URL ; cet article ne propose aucune comparaison de qualité à métriques identiques.

Le cadrage honnête : les stoplists multilingues de jusText sont une vraie capacité, et gratuite. Si ton problème consiste à récupérer des pages dans plusieurs langues plutôt qu’à classer leur prose, c’est un autre achat.

Pour une vue plus large, notre tour d’horizon des API de web scraping couvre les options hébergées et le pilier des scrapers open source couvre les solutions auto-hébergées. Si la sortie est destinée à un modèle, convertir du HTML en Markdown avec Python est l’endroit où se perd le plus de fidélité.

Essayez Thunderbit pour l’extraction de données web

Faut-il utiliser jusText ?

C’est un bon candidat si le corpus est multilingue ou si sa distribution de longueurs de paragraphes bénéficie d’un réglage explicite des seuils. Vérifie ces deux points avant le déploiement.

Les 100 stoplists intégrées sont une vraie caractéristique de conception, mais la précision multilingue n’a pas été testée. La falaise du seuil est réglable ; savoir si une valeur plus basse est acceptable dépend du rappel et de la précision sur le boilerplate mesurés sur un échantillon étiqueté.

Sur ce jeu synthétique anglais, avec les réglages par défaut, newspaper4k n’a laissé fuir aucune unité de boilerplate étiquetée et a récupéré 0.9865 des unités d’article. Cela en fait un candidat de comparaison pour cette charge de travail, pas une recommandation de remplacement universelle.

Essayez Thunderbit pour l’extraction de données web Get Started Free

FAQ

Pourquoi jusText renvoie-t-il une chaîne vide au lieu d’une extraction partielle ? Son classifieur fonctionne en deux passes. Un paragraphe n’obtient la classe good tout seul que s’il dépasse length_high (200 caractères par défaut) ; la seconde passe promeut ensuite les blocs neargood voisins. S’il n’y a aucun paragraphe au-dessus du seuil, il n’existe aucun point d’ancrage, donc tous les candidats retombent dans le boilerplate et le résultat est vide. C’est un comportement tout ou rien par conception, pas un échec à trouver une réponse partielle.

jusText est-il abandonné ? La dernière contribution au dépôt et la dernière publication PyPI datent toutes deux du 2025-02-25 — soit dix-sept mois avant les tests — et les classifieurs PyPI s’arrêtent à Python 3.9. Mais il s’est installé et exécuté sur Python 3.14.2 sans exception, et 9 issues ouvertes ne constituent pas un énorme retard. Lis ces dates comme un risque de maintenance, pas comme une preuve de dysfonctionnement ; le risque pratique est que tu doives corriger toi-même tes bugs.

Pourquoi fuit-il plus de boilerplate que Readability ? Sur ce jeu de test, des blocs voisins ressemblant à de la prose ont franchi la frontière de sortie selon les règles contextuelles par défaut. La promotion dépend de la classe du bloc, de la distance et du contexte, et n’est pas automatique pour chaque voisin. Le résultat mesuré était une fuite de 47 % des unités de boilerplate et 74 tokens contaminés avec les valeurs par défaut.

Comment l’utiliser pour une autre langue ? justext.justext(html, justext.get_stoplist("German")). get_stoplists() renvoie les 100 disponibles. La stoplist est l’entrée lexicale spécifique à la langue d’un classifieur qui utilise aussi la longueur des paragraphes, la densité de liens, la segmentation issue du HTML, la distance aux titres et le contexte des blocs voisins. Cela change l’entrée linguistique ; cela ne valide pas à lui seul la qualité d’extraction en allemand.

Qu’est-ce qui n’a pas été testé ici ? Les pages réelles, dans leur ensemble — il s’agit de jeux de test contrôlés avec unités étiquetées. La capacité multilingue, qui est l’argument principal de la bibliothèque, a été inventoriée à 100 stoplists mais non évaluée sur du texte non anglais. Les cas limites d’encodage, bien que jusText expose encoding, default_encoding et enc_errors. Et max_heading_distance ainsi que les deux seuils de ratio de stopwords sont restés sur leurs valeurs par défaut tout au long du test.

Ke
Ke
CTO chez Thunderbit | Data Scientist senior et expert en ML Forte d’une expérience de près de dix ans en machine learning et en data science, Ke Shen est diplômé de Columbia University et ancien Data Scientist senior chez Walmart Labs. Grâce à une expertise approfondie, reconnue par ses pairs, en Python, R, Java et statistiques, il partage des analyses éprouvées sur le passage d’algorithmes d’IA complexes de la théorie à une architecture prête pour la production.
Table des matières
Thunderbit · Agent IA de données web

Extrayez des données de n'importe quelle page en 1 clic

Approuvé par plus de 250 000 utilisateurs
formule gratuite disponible
De la page web au tableur
Décris ce dont tu as besoin — l'agent IA de Thunderbit l'extrait et l'exporte vers Excel, Google Sheets, Airtable ou Notion. Gratuit pour commencer.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week