O Extrator que Vazou Mais Boilerplate e Ainda Assim Não Perdeu Nenhum Conteúdo Neste Conjunto de Testes

Última atualização em August 14, 2026
O Extrator que Vazou Mais Boilerplate e Ainda Assim Não Perdeu Nenhum Conteúdo Neste Conjunto de Testes
Resumo com IA
Seis bibliotecas, um conjunto de testes anotado, um único avaliador. Nesses 22 testes sintéticos, a biblioteca com o maior vazamento de boilerplate — o Readability da Mozilla, com 23,5% — foi também a única que recuperou todas as unidades de artigo rotuladas. Esse é o trade-off em uma frase, e a maioria dos textos sobre o tema nunca mostra isso, porque quase todos medem só precisão e param por aí. Vai alimentar um modelo e pagar por token? newspaper4k ou goose3. Ambos vazaram zero unidades de boilerplate e zero tokens contaminantes. newspaper4k se você quiser resposta em toda página; goose3 se preferir silêncio a um chute, e suas páginas tiverem parágrafos.

Seis bibliotecas, um conjunto de testes anotado, um único avaliador. Nesses 22 testes sintéticos, a biblioteca com o maior vazamento de boilerplate — o Readability da Mozilla, com 23,5% — foi também a única que recuperou todas as unidades de artigo rotuladas.

Esse é o trade-off em uma frase, e a maioria dos textos sobre o tema nunca mostra isso, porque quase todos medem só precisão e param por aí.

O que foi realmente medido

Cada teste deste conjunto traz a verdade de referência por unidade. Cada bloco da página — os parágrafos do artigo, a navegação, o anúncio, a barra lateral, a seção de comentários, a promoção — é marcado como article ou boilerplate e recebe um token sentinela exclusivo. Então, “o extrator recuperou esta unidade?” é uma checagem exata de substring, não uma pontuação de semelhança. Um sentinela sobrevive no resultado ou não.

Vinte e dois testes, 91 unidades. Seis extratores: Mozilla Readability 0.6.0 (via jsdom 30.0.1), trafilatura 2.2.0, resiliparse 1.0.9, newspaper4k 0.9.6, goose3 3.1.22 e jusText 3.0.2. Python 3.14.2 e Node 22 rodaram na mesma máquina. O artigo não preserva o sistema operacional/CPU, as chamadas exatas, a quantidade de repetições nem a política de aquecimento, então a coluna de tempo é uma observação local, não um benchmark transportável.

Antes de rodar qualquer coisa, segui duas regras. Cada biblioteca Python foi instalada em seu próprio virtualenv vazio, para que seu footprint fosse apenas seu e não herdado do que uma dependência vizinha pudesse puxar. E nenhum runner calcula métrica alguma — todos só despejam o texto bruto extraído, e um único avaliador produz todos os números, para que as seis ferramentas sejam comparadas com a mesma aritmética, e não com seis definições parecidas de “precisão”.

A tabela principal

BibliotecaRecall de artigo (22/22)Vazamento de boilerplatePrecisão de tokens de conteúdoTestes com precisão respondidosTokens contaminantes
Readability1.00000.23530.910911/1135
trafilatura0.98650.05880.941111/114
newspaper4k0.98650.00000.945211/110
resiliparse0.90540.05880.938111/117
jusText0.83780.47060.876010/1174
goose30.82430.00001.000010/110

O recall é agregado sobre os 22 testes. A taxa de vazamento, a precisão agregada de tokens de conteúdo e a contaminação usam os 11 testes que contêm tanto unidades de artigo quanto de boilerplate; “respondidos” mostra quantos desses geraram saída. Os números completos por teste estão em sixway-scores.json.

Uma linha dessa tabela não é padrão. O extract_plain_text do resiliparse tem main_content=False como padrão, e eu o chamei com main_content=True. A diferença não é pequena: no padrão, ele vaza 17 de 17 unidades de boilerplate no conjunto — cada nav, anúncio, barra lateral, thread de comentários e promoção — contra 1 de 17 com a flag ligada. Todas as outras bibliotecas acima foram chamadas com seus padrões. Então a taxa de vazamento de 0,0588 do resiliparse é o que ele faz quando você pede conteúdo principal; extract_plain_text(html) sozinho é outro produto (default-vs-main-content.json).

Leia a primeira e a segunda coluna juntas, porque olhar só uma delas leva você à ferramenta errada.

O Readability nunca erra uma unidade de artigo. Recall perfeito em todos os 22 testes, e é o único a fazer isso. Mas paga por isso: vazou 4 de 17 unidades de boilerplate, gerando 35 tokens contaminantes, quatro vezes a taxa de vazamento da trafilatura. Três desses quatro vazamentos têm a mesma forma — um bloco promocional de classe neutra ao lado do artigo, que seu heurístico de anexar irmãos engole. Se você alimentar a saída em um modelo, estará pagando por esses tokens e o modelo vai lê-los como se fossem artigo.

O newspaper4k é o equilíbrio mais saudável. Vazamento zero, tokens contaminantes zero, recall de 0,9865 e saída em todos os 22 testes. Se eu tivesse que escolher um sem conhecer a carga de trabalho, seria este — e não é o que a maioria das pessoas costuma escolher.

O goose3 tem precisão perfeita e o pior recall do teste. Tudo o que ele retornou como conteúdo era mesmo conteúdo de artigo. Mas também não recuperou absolutamente nada em dois testes e não gerou saída nesses mesmos dois. Precisão perfeita é barata quando você pode simplesmente se recusar a responder.

O número de precisão que favoreceu duas bibliotecas

Esse último ponto vale ser mostrado com clareza, porque é uma armadilha que quase publiquei.

Precisão e F1 aqui dependem de haver saída. Uma biblioteca que devolve string vazia em um teste não soma nada ao numerador nem ao denominador — então “não responder” sai de graça, e a precisão de um extrator conservador parece melhor que a de um extrator completo por nenhum outro motivo além do silêncio.

a precisão agregada do goose3 foi 1,0000 nos 10 testes pontuados em que ele gerou saída. A do jusText foi 0,8760 em 10 de 11. Readability, trafilatura, resiliparse e newspaper4k responderam 11 de 11. A tabela agora mostra esse denominador ao lado da precisão, para que a abstenção não desapareça atrás de uma razão bonita.

Houve uma versão pior disso. Meu primeiro avaliador calculava a média do recall de artigo sobre o mesmo conjunto de 11 testes de fidelidade de conteúdo — o conjunto que exclui os testes sem boilerplate, o que está correto para medir vazamento. Ele reportava o resiliparse com 1,0000 de recall. Considerando os 22 testes, o resiliparse fica em 0,9054, porque no teste cujo artigo vive inteiramente dentro de elementos <li> e não há <p> em lugar nenhum, ele retorna saída e recupera 0 de 6 unidades de artigo. Esse teste não tem boilerplate, então ficou fora da média, e uma falha real ficou escondida atrás de uma nota perfeita.

Onde cada um realmente quebra

TesteO que ele testaQuem não recupera nada
Artigo inteiramente em <li>, sem <p>pressupostos estruturaisresiliparse (0/6), goose3 (sem saída)
Unidade de artigo única com 129 caractereslimiar de conteúdo curtojusText
Dez parágrafos curtos, nenhum longolimiar de conteúdo curtojusText
Documento quase vazioo verdadeiro limite do nulogoose3, jusText

Cada um desses casos é um comportamento específico e reproduzível, e não um vago “extrai pior”:

  • resiliparse e goose3 assumem parágrafos. Aponte qualquer um deles para uma página cujo corpo é uma lista — um changelog, uma especificação, uma FAQ, uma receita — e o resiliparse devolve texto sem nada do conteúdo da lista, enquanto o goose3 não devolve nada. O resiliparse é o mais perigoso dos dois aqui, porque devolver alguma coisa parece sucesso.
  • o jusText tem um penhasco de comprimento, e ele é abrupto. Mais sobre isso abaixo.
  • O documento quase vazio é o único caso em que devolver nada é defensável, então eu não cobraria isso de nenhuma das duas bibliotecas.

jusText: um penhasco, não uma rampa

O jusText gerou saída em 19 de 22 testes e vazou 47% do boilerplate — o maior vazamento do experimento, o oposto da sua reputação. Mas o número interessante é o que me fez rodar tudo de novo.

O jusText classifica cada bloco pela densidade de stopwords contra uma lista de parada de idioma, e depois faz uma segunda passada sensível ao contexto que promove um bloco neargood para good apenas quando ele está ao lado de um bloco já good. Um bloco só vira good sozinho quando ultrapassa length_high, que por padrão é 200 caracteres. Em um documento em que nada cruza essa linha, nada inicia a promoção e a página inteira se degrada para boilerplate.

Eu o varri em um documento cujo maior parágrafo tem 151 caracteres:

length_highParágrafos bonsCaracteres retornados
200 (padrão)00
1508832
1208832
1008832
808832

De zero para 832 caracteres quando exatamente um parágrafo cruza o limiar, e depois nada muda por mais que você reduza o valor. Um único parágrafo acima da linha destrava o documento inteiro.

Antes de concluir isso, eu varri length_low em quatro valores e max_link_density em dois — oito combinações, todas retornando zero. A regra deste projeto é que uma alegação negativa de capacidade precisa de pelo menos três formatos de parâmetro testados, ou da própria mensagem de erro do fornecedor nomeando o campo; um parâmetro improdutivo não basta para concluir algo sobre a biblioteca. Os números estão em justext-length-threshold.json.

Nada disso diz que o jusText extrai mal. Em uma página real, em linguagem natural e com os padrões, ele devolveu 1.190 caracteres de texto limpo de artigo. O que isso diz é que o jusText tem um ajuste documentado que se comporta como um interruptor — e que a posição padrão desse interruptor é errada para documentos com parágrafos curtos.

O que você instala e quanto custa importar

Measured results chart: Install footprint vs cold import

Mesmos testes, mesma máquina, cada biblioteca em seu próprio virtualenv vazio.

BibliotecaPacotessite-packagesImportação a friop50 de extração
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
Readability + jsdom32 (npm)26 MiB0.473 s6.37 ms

Nesta execução, o resiliparse teve os menores valores observados de importação a frio e de mediana de extração: 15 ms e 0,06 ms. Razões exatas entre runtimes distintos exagerariam o que o protocolo incompleto permite concluir, especialmente porque a extração individual mais lenta chegou a 1.098 ms. Antes de usar esses números para dimensionar serverless, seriam necessárias distribuições separadas de startup, primeira chamada e estado estável.

trafilatura e resiliparse empatam em qualidade — 0,9697 contra 0,9681 em F1 de tokens de conteúdo, com a mesma taxa de vazamento de 0,0588 — e eu não vou declarar vencedor por uma diferença tão pequena. Em footprint, porém, eles não chegam perto: 21,0 MiB contra 69,9 MiB, 5 pacotes contra 17. O trade-off real é a cegueira do resiliparse para listas versus os três requisitos extras da trafilatura.

Dois bugs no meu próprio ambiente de teste, encontrados antes da publicação

A comparação acima quase não aconteceu, e o motivo vale mais do que qualquer linha isolada da tabela.

O conjunto de testes não conseguia enxergar duas das seis bibliotecas. Os testes originais escrevem cada unidade como uma sequência de tokens sem sentido exclusivos — zzart01vf64 zzart01v56i — exatamente o que torna o recall exato. Só que isso também significa que os testes não contêm nenhuma palavra funcional em inglês. Readability, trafilatura e resiliparse decidem estruturalmente, pelo DOM, então não foram afetadas. goose3 e jusText decidem lexicalmente, contando stopwords, e não havia nada para contar: ambas retornaram string vazia em todos os 22 testes.

Uma tabela com duas bibliotecas zeradas teria parecido autoritária e não significaria nada. Eu conferi antes de escrever isso, numa página real: o goose3 retornou 1.017 caracteres e o jusText 1.190. As bibliotecas estavam bem. O ambiente de teste é que não conseguia representá-las.

Então os testes foram reconstruídos com prosa em inglês carregando os sentinelas — mesma estrutura, mesmas classes, mesmas posições no DOM, mesmos limites de unidade, mesmos sentinelas, 1.568 tokens trocados um a um. O goose3 saiu de 0 para 20 de 22.

Depois a reconstrução quebrou duas coisas próprias, e ambas foram culpa minha. Uma palavra em inglês tem em torno de seis caracteres; zzart01vf64 tem em torno de doze. Trocar uma pela outra cortou pela metade cada unidade — 21.646 caracteres de texto de unidade viraram 10.986, e a maior unidade caiu de 1.513 para 622. Isso reescreveu silenciosamente os testes cujo propósito inteiro é medir comprimento. O jusText, cujo comportamento é um penhasco de comprimento, caiu de 19 de 22 para 6 de 22 só por causa disso. Se eu tivesse publicado a versão reduzida, o número do jusText estaria errado por um fator de três, e na direção que o faria parecer uma biblioteca pior.

A segunda: usar um único corpus compartilhado para todas as unidades restaurou a densidade de stopwords, mas destruiu a propriedade da qual a pontuação em nível de token depende. Os vocabulários de artigo e boilerplate precisam ser disjuntos, ou “tokens extraídos que são tokens de boilerplate” acaba contando a palavra the. Dez de 22 testes ficaram com vocabulários sobrepostos, contra zero no original. A correção foi sufixar palavras de conteúdo por unidade e deixar as palavras funcionais sem marcação — stopwords reais para as bibliotecas lexicais contarem, vocabulário de conteúdo disjunto para o avaliador.

É também por isso que as colunas de nível de token aqui se chamam content_token_* e não foram reaproveitadas dos números publicados de Readability versus trafilatura. Elas medem outra coisa, apenas sobre palavras de conteúdo, e citar uma como se fosse a outra estaria errado.

Durante a reconstrução, apareceu mais uma coisa que não era minha: três testes de densidade de links colocam </a> no meio de uma palavra<a href="/x">zzsibp015qlhf zzsi</a>bp015qbht — porque o anchor foi posicionado por offset de caractere para atingir uma razão exata. O texto renderizado não muda, então a pontuação original nunca percebeu, mas qualquer extrator que opere por elemento e não por trecho de texto vê dois fragmentos onde os outros veem uma palavra. Corrigido, com o delta de caracteres ligado registrado em vez de engolido em silêncio.

Quem deve usar o quê

Vai alimentar um modelo e pagar por token? newspaper4k ou goose3. Ambos vazaram zero unidades de boilerplate e zero tokens contaminantes. newspaper4k se você quiser resposta em toda página; goose3 se preferir silêncio a um chute, e suas páginas tiverem parágrafos.

Otimizando um caminho Python sensível à latência? Inclua o resiliparse no comparativo. Ele teve a menor importação e a menor mediana de extração observadas aqui e ficou muito perto da trafilatura em qualidade — mas só com main_content=True, que não é o padrão. Verifique primeiro layouts pesados em listas e não transforme esses tempos locais em uma razão exata de velocidade entre runtimes diferentes.

Arquivamento, ou qualquer caso em que perder conteúdo seja pior do que sobrar conteúdo? Readability. É o único que recuperou todas as unidades de artigo em todos os testes, e 35 tokens fora do lugar é um preço baixo se a alternativa for perder um parágrafo.

Trabalho multilíngue? jusText é um candidato a considerar porque vem com listas de stopwords por idioma. Este estudo não testou extração multilíngue, então esse recurso é motivo para avaliá-lo, não prova de que ele vence. Teste length_high contra comprimentos de parágrafo representativos.

Qualquer coisa que não seja um artigo? Nenhuma delas. Todas partem da suposição de que a página tem um único corpo principal de prosa, e uma lista de produtos, uma página de resultados de busca ou um dashboard quebram essa suposição de formas que nenhum parâmetro conserta.

Onde uma API gerenciada entra

Tudo acima são bibliotecas que você executa: você fornece HTML e recebe texto. Os modos de falha observados variam conforme a forma da página, então valide os padrões escolhidos contra o seu corpus. Extração estruturada de campos e fetching/renderização ficam fora desta comparação.

Nota do autor: Thunderbit é nosso serviço gerenciado para fluxos que começam com URL e exigem saída estruturada. Ele não passou por estes testes, então nenhuma comparação de qualidade está sendo sugerida. A decisão relevante é se você já tem o HTML e quer um extrator local de texto, ou se quer fetching/renderização e operação sob gestão de um serviço.

O enquadramento honesto: se você já tem o HTML e quer texto, uma destas seis é grátis e boa, e esta tabela mostra qual. Se você está coletando páginas em escala, ou quer linhas em vez de prosa, isso é outra compra.

Se você estiver escolhendo entre captadores hospedados, nosso resumo de APIs de web scraping cobre esse campo e nossa comparação de custo de APIs de SEO e dados mostra o que cada uma cobra. Para o lado auto-hospedado, o pilar de scrapers open source é a visão mais ampla, e se o que você realmente precisa é Markdown em vez de texto puro, converter HTML para Markdown em Python é onde a maior parte da perda acontece.

Experimente o Thunderbit para Extração de Dados da Web

Veredito

Não há vencedor, e uma tabela que apontasse um seria mentindo sobre um trade-off real.

Monte um pequeno corpus de validação antes de escolher: inclua artigos feitos só de listas, parágrafos curtos, promoções vizinhas, uma página quase vazia e exemplos em que retornar nada seja melhor do que contaminar a saída. Meça separadamente recuperação de artigo, vazamento de boilerplate e abstenção. Nestes testes, Readability favoreceu recall, newspaper4k entregou a linha mais equilibrada e resiliparse foi um candidato de baixa latência com um ponto cego para conteúdo em lista; esses rótulos não devem ser extrapolados para formas não testadas sem validação.

O que eu realmente te diria é mais estreito do que tudo isso: rode os testes contra os formatos de página que você usa antes de escolher. Duas das seis bibliotecas não conseguiam enxergar o conjunto de teste que eu comecei usando, e uma delas marcou recall perfeito escondendo uma falha total. Uma tabela comparativa é um ponto de partida para isso, não um substituto.

Experimente o Thunderbit para Extração de Dados da Web Get Started Free

Perguntas frequentes

Esses números são comparáveis aos benchmarks publicados para essas bibliotecas? Não, e eu não os citaria dessa forma. Estes são testes controlados com unidades sintéticas, porém rotuladas, então todas as seis viram bytes idênticos e a comparação entre elas é justa. Números publicados, como o benchmark de extração de artigos da scrapinghub, usam corpora do mundo real, que medem outra coisa — e mais difícil. Use esta tabela para comparar estas seis entre si, não contra um número de um paper.

Por que a taxa de vazamento do Readability é tão maior que a da trafilatura se ambas são baseadas em DOM? Porque cada uma desenha o limite em um lugar diferente. Três dos quatro vazamentos do Readability são blocos promocionais de classe neutra ao lado do artigo, que seu heurístico de anexar irmãos traz junto com a ideia de que conteúdo longo e com poucos links, se estiver adjacente, provavelmente faz parte da história. Muitas vezes faz. Nestes testes, era uma promoção. A trafilatura é mais rígida sobre o que anexa e vazou uma dessas mesmas unidades.

Devo confiar nos números de precisão do goose3 e do jusText? Só junto com a contagem de amostras. Ambos foram pontuados em 10 dos 11 testes que têm unidades de artigo e boilerplate, porque não responderam em um deles, e um teste sem saída não contribui para nenhum lado da razão. A precisão 1,0000 do goose3 é real para as páginas em que ele respondeu; o recall 0,8243 dele em todos os 22 testes é a outra metade do mesmo fato.

O limiar de comprimento do jusText importa em páginas reais? Depende inteiramente do tamanho dos seus parágrafos. Um artigo de notícia com parágrafos de 300 caracteres cruza length_high logo no primeiro e se comporta normalmente — é por isso que o jusText devolveu 1.190 caracteres limpos em uma página real com as configurações padrão. Uma página de parágrafos curtos, itens de lista ou descrições de produtos talvez nunca cruze esse ponto, e então o jusText devolve string vazia em vez de uma resposta parcial. Defina isso explicitamente em vez de descobrir em produção.

O que não foi testado aqui? Páginas reais, nenhuma. Extração multilíngue, apesar de as stoplists serem o principal argumento do jusText. Memória sob carga. Qualquer página que não seja um artigo — sem listas de produtos, sem resultados de busca, sem dashboards. Casos extremos de codificação. E os ecossistemas Node e Python foram comparados pelo comportamento das bibliotecas, não pelo desempenho do runtime; então os números em milissegundos entre esses ambientes devem ser lidos como ordens de grandeza, não como razões precisas.

Ke
Ke
CTO na Thunderbit | Cientista de Dados Sênior e Especialista em ML Com quase uma década de experiência em machine learning e data science, Ke Shen é ex-aluno da Columbia University e foi Cientista de Dados Sênior na Walmart Labs. Com profunda experiência, reconhecida pelos pares, em Python, R, Java e Estatística, ele compartilha insights testados em batalha sobre como levar algoritmos complexos de IA da teoria à arquitetura pronta para produção.
Sumário
Thunderbit · Agente de dados web com IA

Extraia dados de qualquer página em 1 clique

Aprovado por mais de 250.000 usuários
plano gratuito disponível
Da página web para a planilha
Descreva o que você precisa — o Agente de IA da Thunderbit extrai e exporta para Excel, Google Sheets, Airtable ou Notion. Comece grátis.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week