html2text instala apenas um pacote, ocupando 0,2 MiB — nove vezes menos que a alternativa Python mais próxima e quarenta vezes menos que a opção em Node. Ele concluiu as quatro páginas da suíte de conversão e preservou todos os 16 body probes registrados. Esses probes verificam se determinadas strings do corpo continuam presentes; eles não avaliam hierarquia, aninhamento de listas, destinos de links, conteúdo repetido nem a fidelidade total de tabelas.
Ele também é licenciado sob GPL-3.0-or-later, a única característica nesta comparação que não aparece em nenhum benchmark e que pode eliminar uma biblioteca por completo.
O que é o html2text
html2text é uma biblioteca Python que transforma HTML em texto simples com aparência de Markdown. Sua origem remonta ao trabalho original de Aaron Swartz, e a linha mantida atualmente está na versão 2025.4.15 — um lançamento datado de abril de 2025, com 2.168 estrelas no GitHub, 95 issues abertas e o último push em outubro de 2025.
Referência oficial: repositório oficial do html2text.
import html2text
h = html2text.HTML2Text()
h.body_width = 0 # veja abaixo; o padrão pode te surpreender
md = h.handle(html)
pip install html2text baixa 1 pacote e 0,2 MiB, com tempo de importação a frio de 0,077 s. Zero dependências. Em uma imagem de container ou em uma camada de Lambda, isso faz uma diferença relevante em comparação com os 1,8 MiB do markdownify e os 8,8 MiB do turndown.
O padrão que muda todos os números

body_width tem padrão 78. O html2text faz quebra automática de linha em toda a saída a cada 78 caracteres, a menos que você desative isso.
Isso até faz sentido para uma biblioteca cujo objetivo original era produzir texto legível para terminais e e-mail. Mas é o padrão errado para qualquer coisa que vá alimentar um modelo ou um diff, porque novas quebras de linha alteram a tokenização, quebram URLs longas no meio e tornam a comparação da saída pouco confiável.
Ajustei body_width = 0 em tudo abaixo e estou destacando isso em vez de esconder: com a quebra ativada, toda contagem de caracteres e tokens neste artigo seria diferente. Se você estiver fazendo benchmark de conversores por conta própria, essa é a configuração que pode tornar seus números comparáveis ou não, sem avisar.
A medição
Executei o html2text em uma suíte nomeada de conversão com quatro páginas, também usada na comparação com markitdown, com probe strings pré-registradas — strings selecionadas do corpo que precisam sobreviver, e strings de boilerplate cuja presença mostra que o chrome da página foi preservado. Os mesmos quatro arquivos, os mesmos probes, o mesmo critério para todos os quatro conversores. A sobrevivência dos probes mede a presença das strings registradas, não a correção estrutural; por isso as colunas de tabelas e links são analisadas separadamente.
| Conversor | Body probes | Caracteres de saída | Tokens (o200k) | Linhas de tabela Markdown | Links |
|---|---|---|---|---|---|
| html2text | 16/16 | 76.452 | 21.176 | 32 | 545 |
| markdownify | 16/16 | 76.868 | 21.062 | 36 | 599 |
| markitdown | 16/16 | 76.995 | 21.336 | 36 | 598 |
| turndown | 16/16 | 95.188 | 26.236 | 0 | 611 |
fourway-scores.json. Quatro fixtures, tokens contados com o200k_base.
Menor saída entre os quatro, com 76.452 caracteres, e praticamente empatado em tokens com markdownify e markitdown — 21.176 contra 21.062 e 21.336, uma diferença de 1,3% que eu não chamaria de diferença relevante.
32 linhas de tabela contra 36 do markdownify e 36 do markitdown na suíte de quatro páginas. A diferença de quatro linhas aparece no fixture irregular da Wikipedia, não na diferença de formatação com pipes externos descrita mais adiante.
Menos links, com 545, contra 598 a 611 dos demais. Vale conferir nas suas próprias páginas se a preservação de links importa — é a única coluna em que o html2text fica claramente abaixo do grupo, e não dentro dele.
As tabelas que meu regex não enxergou

Essa parte vale ser contada porque eu quase publiquei a conclusão errada.
Meu primeiro contador de linhas de tabela exigia pipes no início e no fim — ^\|.*\|$. Com essa regra, o html2text marcava 1 linha de tabela em cinco arquivos: a suíte de quatro páginas mais um fixture sintético separado de tabela complexa. Esse contador media um estilo específico de Markdown, não tabelas.

Ele faz isso. A saída vem assim:
Team Name | Year | Wins | Losses | Win %
---|---|---|---|---
Boston Bruins | 1990 | 44 | 24 | 0.55
Sem pipes externos. Isso é sintaxe convencional de tabela com pipes, mas o harness não validou isso entre renderizadores de Markdown. Para um regex que espera o estilo com pipes externos, isso passa invisível. Ao reescrever o contador para procurar uma sequência de linhas com pipes e uma linha separadora no meio, o html2text passou de 1 linha para 32 na suíte de quatro páginas e 91 no total dos cinco arquivos.
Então a conclusão não é que o html2text não lida com tabelas. A conclusão é que dois desses quatro conversores usam pipes externos e um não usa, o que importa se você faz pós-processamento do Markdown com seus próprios padrões. Isso é algo realmente útil de saber, e eu teria perdido totalmente se tivesse confiado no primeiro número.
O que ele remove, e onde perde linhas
Dois resultados que puxam em direções opostas.
Ele remove <script> e <style>. Contando marcadores que só aparecem dentro desses elementos, a saída do html2text nas fixtures traz zero marcadores de script e zero marcadores de style. A do turndown traz 10 e 84 — no fixture da Wikipedia, oito linhas da configuração JavaScript inline do MediaWiki e CSS somando 14.644 caracteres (script-style-stripping.json). Para saída voltada a modelos, essa foi a maior fonte observada de texto evitável nesse fixture. Nenhum modelo de custo downstream foi medido.
Ele perde linhas de tabela na página mais difícil. A suíte de quatro páginas se reconcilia assim:
| Fixture | html2text | markdownify |
|---|---|---|
| Books to Scrape | 0 linhas | 0 linhas |
| Quotes to Scrape | 0 linhas | 0 linhas |
| Hockey statistics | 27 linhas | 27 linhas |
| Wikipedia | 5 linhas | 9 linhas |
| Total em quatro páginas | 32 linhas | 36 linhas |
O fixture sintético complexo separado adiciona 59 linhas ao html2text e 62 ao markdownify, levando os totais em cinco arquivos para 91 e 98. Ele não faz parte da comparação principal de quatro páginas. Ambos concordam na tabela limpa de hóquei. Na Wikipedia, onde as tabelas são aninhadas e irregulares, o html2text emite cinco linhas, enquanto o markdownify emite nove.
O padrão é este: tabelas simples, idênticas; tabelas complicadas, o html2text preserva menos. Se suas páginas tiverem tabelas do tipo Wikipedia, teste isso antes de adotar. Se forem como as de uma página de estatísticas, os dois são intercambiáveis nesse ponto.
A licença
| Biblioteca | Licença | Pacotes | Disco |
|---|---|---|---|
| html2text | GPL-3.0-or-later | 1 | 0,2 MiB |
| markdownify | MIT | 5 | 1,8 MiB |
| turndown | MIT | 3 (npm) | 8,8 MiB |
Referência oficial: html2text no PyPI.
Confirmado em três lugares: nos metadados do PyPI, no repositório do GitHub e no arquivo METADATA do pacote instalado, que traz License-Expression: GPL-3.0-or-later.
O que isso significa depende de como o software é integrado, distribuído e veiculado. Uso interno ou apenas via rede costuma ser um cenário GPL diferente de distribuir software que inclui ou combina com o pacote, mas este artigo não é uma análise jurídica. Equipes que distribuem software devem pedir que o time jurídico revise o modelo exato de integração e distribuição.
A parte incômoda é a correlação. A biblioteca com a menor pegada, aquela que você escolheria justamente para manter um artefato distribuível pequeno, é a que tem a licença que mais restringe distribuição. As duas alternativas são MIT.
Não sou advogado e isso não é aconselhamento jurídico — é só o fato, com fonte, porque é a propriedade com maior chance de importar e menor chance de aparecer em uma tabela de comparação.
Manutenção
Último lançamento 2025.4.15, último push no repositório em outubro de 2025 — cerca de dez meses antes dos testes, com 41 releases atrás dele. requires_python >= 3.9, e ele instalou e rodou normalmente no Python 3.14.2.
Isso é mais tranquilo que o markdownify (último lançamento seis semanas antes dos testes) e o turndown (quatro meses), e bem mais ativo do que não ter manutenção nenhuma. Para uma biblioteca que converte HTML em texto — um problema que não muda tanto — uma lacuna de dez meses parece estabilidade, não abandono. As 95 issues abertas são o sinal mais importante aqui, e merecem uma passada de olhos para qualquer coisa parecida com o seu caso de uso antes de você se comprometer.
Memória, e o que HTML quebrado faz com ela
Aqui há duas perguntas operacionais medidas separadamente.
O contexto mais amplo do stress test está na comparação de memória e HTML malformado entre dez bibliotecas.
Pico de memória residente, via /usr/bin/time -l, um processo novo por célula — o piso de importação é o custo da biblioteca carregada e ociosa; os picos incluem o documento.
| Biblioteca | Runtime | Piso de importação | Pico 226 KB | Pico 10 MB |
|---|---|---|---|---|
| html2text | python3.14 | 18.7 | 19.9 | 71.2 |
| pyquery | python3.14 | 30.3 | 33.9 | 172.5 |
| resiliparse | python3.14 | 20.5 | 25.1 | 225.1 |
| markdownify | python3.14 | 23.9 | 28.9 | 278.5 |
| goose3 | python3.14 | 44.1 | 52.4 | 398.5 |
| cheerio | node22 | 66.8 | 76.5 | 398.5 |
| justext | python3.14 | 30.3 | 36.6 | 431.2 |
| newspaper4k | python3.14 | 52.6 | 61.8 | 668.5 |
| trafilatura | python3.14 | 52.5 | 64.8 | 927.1 |
| turndown | node22 | 47.8 | 68.4 | 2947.1 |
memory-results.json. As bases de Python e Node não são comparáveis entre si; o interpretador está dentro de ambas.
html2text é a entrada mais leve aqui em ambos os eixos medidos. Seu piso de importação de 18,7 MiB e pico de 71,2 MiB no fixture de 10 MiB resultam em um RSS incremental de 52,5 MiB: (71.2 - 18.7) / 10 = 5.25× o tamanho do fixture. Compare isso com as linhas absolutas e incrementais da tabela, mantendo em mente a ressalva sobre as bases de Python/Node.
HTML quebrado. Doze documentos quebrando exatamente uma coisa cada — tags não fechadas, elementos inline mal aninhados, atributos sem aspas com espaços, fechamentos soltos, ausência total de <html>, atributos duplicados, documento truncado no meio de uma tag, entidades inválidas, um <script> não fechado, uma declaração de charset mentirosa, um comentário contendo markup e 600 níveis de aninhamento — mais dois controles bem formados com tamanhos correspondentes, porque “não retornou nada” só diz algo sobre malformação se a biblioteca também não permanecer silenciosa em um documento limpo do mesmo tamanho.
O html2text lançou exceção em 0 de 14 e não retornou nada em 0, recuperando 33/33 sentinelas nas fixtures quebradas (malformed-results.json). Um fixture fica fora dessa contagem: pelo HTML5, tudo após um <script> não fechado é conteúdo de script, então perdê-lo ali está correto, e recuperá-lo é que seria o desvio.
Prós e contras
A favor. Um pacote, 0,2 MiB, zero dependências — de longe o menor da comparação. Menor saída entre os quatro e praticamente empatado em tokens com markdownify e markitdown. Emite uma sintaxe reconhecível de tabela com pipes. Roda no Python 3.14. Linha de evolução longa e estável.
Contra. GPL-3.0-or-later, o que as alternativas não são. body_width=78 por padrão, o que faz quebra rígida de linhas e altera medições ou diffs, a menos que você desligue isso. Menor preservação de links (545 contra 598–611). As tabelas usam o estilo sem pipes externos, o que quebra regex ingênuas em pós-processamento. Noventa e cinco issues abertas e um ritmo de releases mais lento que o do markdownify.
Quem deve usar e quem não deve
Use o html2text quando o orçamento de dependências for realmente apertado e o modelo pretendido de integração e distribuição já tiver passado por revisão de licença. Um pacote com zero dependências é uma vantagem operacional real: menos superfície para auditar e implantar.
Defina body_width = 0 na primeira linha, a menos que você queira especificamente texto simples com quebras automáticas.
Evite-o se você distribui software e copyleft é um problema — o markdownify é MIT, empata em tokens e iguala o markitdown em tabelas, com 1,6 MiB a mais. Evite também se a preservação de links for importante para você, já que ele foi o que menos preservou. E evite se suas ferramentas downstream assumem pipes externos nas linhas de tabela.
Onde uma API gerenciada se encaixa
O html2text converte HTML que você já possui. Ele não busca páginas, não renderiza JavaScript e não lida com camada anti-bot — nenhum dos quatro conversores faz isso, e em muitos alvos reais essa é a parte mais difícil do trabalho.
Para as mesmas fixtures em todos os cinco conversores, veja a comparação de HTML para Markdown em cinco vias.
Um serviço hospedado de fetch/render/extraction, incluindo o nosso Thunderbit, atua em outra camada. O Thunderbit não foi benchmarkado aqui. A fronteira relevante é conversão de HTML fornecido versus um serviço que obtém e processa uma URL; este artigo não traz comparação de qualidade, latência ou custo na mesma métrica.
O enquadramento justo: se você já tem o HTML, quer Markdown e a GPL não é um problema para a forma como você distribui, o html2text é gratuito e notavelmente pequeno. Se você está buscando páginas, ou quer linhas em vez de texto corrido, isso já é outra compra.
Para o panorama mais amplo, nosso resumo de APIs de web scraping cobre opções hospedadas, e o guia de scrapers open source cobre as soluções auto-hospedadas. Convertendo HTML para Markdown em Python é o passo a passo prático.
Experimente o Thunderbit para extração de dados da web
Vale a pena usar html2text?
É um forte candidato quando o tamanho importa, a quebra automática está desativada de propósito e o modelo de distribuição passa pela revisão de licença.
A contagem de tokens ficou dentro de 1,3% do markdownify e do markitdown na suíte de quatro páginas. Isso não significa que a qualidade geral seja equivalente: o html2text preservou menos links e menos linhas na Wikipedia irregular. Dois detalhes operacionais importam imediatamente: body_width = 0 e o estilo de tabela sem pipes externos.
Se a revisão da GPL o eliminar, o markdownify é MIT, teve tamanho de saída e contagem de tokens parecidos aqui, preservou mais links e mais linhas de tabelas irregulares, e usou 1,6 MiB a mais em disco neste ambiente.
Experimente o Thunderbit para extração de dados da web Get Started Free
FAQs
O html2text converte tabelas?
Sim. Meu primeiro contador disse que ele produzia uma linha de tabela em cinco arquivos, e esse contador estava errado — ele exigia pipes no começo e no fim, enquanto o html2text emite Team Name | Year | Wins sem eles. Isso é Markdown convencional de tabela com pipes, embora este harness não tenha feito um teste de compatibilidade entre renderizadores. Com o contador corrigido, o html2text produziu 32 linhas contra 36 do markdownify na suíte de quatro páginas e 91 contra 98 quando o fixture complexo separado é incluído.
O que body_width faz, e por que mudar isso?
Ele faz quebra rígida de linhas em 78 caracteres por padrão, uma escolha sensata para texto simples legível em terminal e ruim para quase todo o resto. A quebra insere novas linhas no meio da frase, separa URLs longas e altera a tokenização. Todos os números desta análise usaram body_width = 0; com o padrão, seriam todos diferentes.
A licença GPL é uma restrição real?
Depende do modelo exato de integração e distribuição. Uso interno ou apenas via rede e distribuição de software são cenários de análise diferentes, mas este artigo não determina o resultado jurídico. Equipes que distribuem software devem pedir ao jurídico que revise os termos da GPL-3.0-or-later; o markdownify e o turndown são MIT. A expressão da licença do html2text foi confirmada nos metadados do PyPI, no GitHub e no arquivo METADATA do pacote instalado.
Um lançamento de abril de 2025 é um problema? Provavelmente não, por si só. Conversão de HTML para texto é um problema estável, a biblioteca instalou e rodou corretamente no Python 3.14.2, e há 41 releases atrás dela. As 95 issues abertas são o número que eu realmente verificaria — veja se há algo parecido com seu input antes de adotar, porque um repositório silencioso pode significar que você será o próximo a corrigi-lo.
O que não foi testado aqui?
Quatro fixtures de conversão e um fixture separado de tabela complexa ainda formam uma suíte pequena. O teste cobriu doze documentos sintéticos malformados, mais dois controles: o html2text lançou exceção em 0/14, retornou vazio em 0/14 e recuperou todas as 33 sentinelas pontuadas. Ele não cobriu páginas reais danificadas, padrões mais amplos de malformação, listas aninhadas, listas de definição, notas de rodapé nem matemática. Toda a superfície de opções — ignore_links, ignore_images, unicode_snob, single_line_break e o restante — permaneceu no padrão, exceto body_width. A diferença de links foi observada, mas não diagnosticada, e o round-trip de Markdown não foi testado.


