From html-text¶
html-text (Zyte) extracts the visible text from a page. It strips
<script> and <style>, inserts newlines around block tags, and optionally guesses block layout so the output
reads like the rendered page rather than a raw token dump. Zyte ships it as a feature-extraction step for web scraping
and machine-learning pipelines, where the text feeds classifiers or search indexes. Under the hood it builds an lxml
tree and walks it in Python, with parsel-bound helpers for reading text off an already-selected node.
turbohtml covers the same ground with to_text(): one fully type annotated C tree walk that renders
layout-aware plain text, plus the strings / stripped_strings iterators
for the collapsed word stream html-text returns with layout guessing off.
turbohtml vs html-text¶
Dimension |
turbohtml |
html-text |
|---|---|---|
Scope |
Full WHATWG parser, DOM, CSS/XPath selection, and text/Markdown/HTML serialization |
Visible-text extraction from an lxml tree |
Feature breadth |
Layout profiles, link and image rendering, aligned tables, wrap width, bullet markers, annotated text |
Layout guessing on/off, punctuation-space guessing, custom newline tag sets |
Performance |
Single C tree walk (see inscriptis-shared benchmark below) |
lxml tree build plus a Python walk |
Typing |
Fully annotated with shipped stubs |
Untyped |
Dependencies |
None (self-contained C extension) |
lxml and parsel |
Maintenance |
Active, broad HTML surface |
Zyte-maintained, stable and narrow in scope |
Feature overlap¶
Port these 1:1:
extract_text(html)(layout-guessed visible text) maps toparse(html).to_text().extract_text(html, guess_layout=False)(collapsed word stream) maps to" ".join(parse(html).stripped_strings).The raw word list html-text collects maps to the
strings/stripped_stringsiterators, or thetextconcatenation.
What turbohtml adds¶
Column-aligned table layout.
to_text()renders<table>as aligned columns; html-text emits the cells as a flat text run.A
PlainTextconfig that renders link targets (links="inline"/"footnote"), image alt text (images), wrapped prose (width), and custom list markers (bullet), none of which html-text produces.Annotated text through
to_annotated_text(), pairing the text with labeled spans.WHATWG-conformant parsing of malformed markup, where html-text inherits lxml’s non-spec recovery.
No lxml or parsel dependency, full type annotations, and the surrounding DOM, selection, and serialization surface.
What html-text has that turbohtml does not¶
Parsel integration.
cleaned_selectorandselector_to_textread text off aparsel.Selector. turbohtml has no parsel binding; select the node with turbohtml’s own CSS/XPath and callto_text()on it (see parsel).Punctuation-space guessing on the word stream. html-text’s
guess_punct_spacesuppresses spaces before punctuation when joining inline text. Joiningstripped_stringswith a single space has no equivalent toggle;to_text()handles inline concatenation correctly, so prefer it when the spacing matters.Per-tag newline control. html-text accepts
newline_tags/double_newline_tagssets. turbohtml selects spacing through thelayoutprofile ("extended"/"strict") rather than a per-tag override; no equivalent for renaming which tags break lines.
Performance¶
to text |
turbohtml |
|
|---|---|---|
layout-aware text — article (2 KiB) |
1.63 µs |
101 µs (61.6x) |
layout-aware text — table (4 KiB) |
10.1 µs |
268 µs (26.5x) |
collapsed word stream — collapsed (2 KiB) |
2.16 µs |
106 µs (49.0x) |
The same text benchmark that backs the inscriptis comparison also runs html-text’s extract_text:
to_text() walks the tree once in C, where html-text builds an lxml tree and collects its text in
Python. html-text skips the column-aligned table layout to_text() renders, so its margin behind
turbohtml narrows on table-heavy input while staying near an order of magnitude. The word stream row turns layout
guessing off (extract_text(guess_layout=False)) against joining the stripped_strings
iterator, the collapsed visible text both return without layout.
How to migrate¶
Swap the import and the call:
# html-text
import html_text
html_text.extract_text(html) # layout-guessed visible text
html_text.extract_text(html, guess_layout=False) # collapsed word stream
# turbohtml
import turbohtml
turbohtml.parse(html).to_text() # layout-aware text
" ".join(turbohtml.parse(html).stripped_strings) # collapsed word stream
API mapping:
turbohtml |
|
|---|---|
|
|
|
|
the |
a |
the raw word list |
|
the parsel-bound |
out of scope; see parsel |
doc = parse("<p>Hello <b>bold</b> world</p>")
print(doc.to_text())
print(list(doc.stripped_strings))
Hello bold world
['Hello', 'bold', 'world']
Gotchas and pitfalls¶
extract_text’sguess_layouttoggle maps to thelayout("extended"/"strict") profile on aPlainTextconfig passed toto_text(); the inscriptis page covers the fullPlainTextoption surface.For the collapsed word stream html-text returns with layout guessing off, join the
stripped_stringsiterator rather than reaching forto_text.Joining stripped strings puts a space between every token, including before punctuation. html-text’s
guess_punct_spaceavoids that; when the spacing matters, callto_text(), which concatenates inline text without the spurious spaces.html-text emits table cells as a flat run, so its output differs from the column-aligned block
to_text()renders on table-heavy pages.html-text’s parsel-bound helpers (
cleaned_selector,selector_to_text) are out of scope; the parsel page covers reading text off a selected node.