################### Exporting to text ################### Once you have the node you want, :meth:`~turbohtml.Node.to_markdown` turns it into `GitHub-Flavored Markdown `_ in one call, so a scraping script ends with Markdown instead of a tag soup: .. testcode:: import turbohtml doc = turbohtml.parse("

Tea

Steep green tea for 3 minutes.

") print(doc.find("article").to_markdown()) .. testoutput:: ## Tea Steep *green* tea for **3** minutes. ********************** Pull out the article ********************** A real page wraps that prose in navigation, sidebars and footers. When you only want the article and do not know its selector, :meth:`~turbohtml.Node.main_content` finds the dominant content element for you by scoring the tree, and :meth:`~turbohtml.Node.main_text` hands you its text directly: .. testcode:: page = turbohtml.parse( "" "

Tea

" "

Steeping green tea for three minutes draws out its flavor without turning it bitter.

" "" ) print(page.main_content().tag) print(page.main_text()) .. testoutput:: main Tea Steeping green tea for three minutes draws out its flavor without turning it bitter. ******************** Sanitize a snippet ******************** Export runs the other way too: when the HTML comes from someone else, clean it with :func:`turbohtml.clean.sanitize` before you embed it. The default policy keeps a safe subset of tags, drops event handlers and dangerous URL schemes, and escapes the elements it removes rather than discarding their text: .. testcode:: from turbohtml.clean import sanitize print(sanitize('x bold')) .. testoutput:: x bold<script>bad()</script> If the target is an XHTML dialect rather than HTML -- an ePub document, or an XML template that will not accept a bare ``
`` -- set ``xml`` on the :class:`~turbohtml.clean.Policy` and the cleaned tree serializes as well-formed XML, every empty element self-closed: .. testcode:: from turbohtml.clean import sanitize, Policy print(sanitize("

one
two

", Policy(tags=frozenset({"p", "br"}), xml=True))) .. testoutput::

one
two

******************* Shrink the output ******************* When you serialize a page to ship it, pass a :class:`~turbohtml.Minify` layout to drop the whitespace, optional tags and quotes the parser can put back. Hand its ``minify_js`` a :class:`~turbohtml.clean.JSMinify` and inline ``') print(doc.serialize(Html(layout=Minify(minify_js=JSMinify())))) .. testoutput::

Hi

********************** Emit well-formed XML ********************** To hand the tree to an XML toolchain instead of a browser, set ``xml=True`` on the :class:`~turbohtml.Html` config. Every empty element self-closes, foreign SVG and MathML subtrees carry their namespace declarations, and text and attribute values follow the XML escaping rules, so the output parses with any XML reader: .. testcode:: from turbohtml import Html doc = turbohtml.parse("

a & b

") print(doc.find("p").serialize(Html(xml=True))) .. testoutput::

a & b

****************************** Canonicalize for a signature ****************************** When a document has to hash to the same bytes on both sides of a signature, serialize it to Canonical XML with :meth:`~turbohtml.Node.canonicalize` and a :class:`~turbohtml.Canonical` config. Attributes are reordered, redundant namespace declarations are dropped, empty elements become start-end pairs, and character references are normalized, so two trees with the same content produce identical bytes: .. testcode:: from turbohtml import Canonical doc = turbohtml.parse("

hi & bye

") print(doc.find("p").canonicalize()) print(doc.find("p").canonicalize(Canonical(exclusive=True))) .. testoutput:: b'

hi & bye

' b'

hi & bye

' Those renderers all normalize the markup. When instead you want to edit one thing and leave the rest of the source untouched, parse with ``source_locations=True`` and call :meth:`~turbohtml.Node.to_source`: it re-emits the verbatim bytes of everything you did not change and reserializes only what you did. .. testcode:: doc = turbohtml.parse("

Steep the tea.

", source_locations=True) doc.find("b").attrs["data-term"] = "1" print(doc.find("p").to_source()) .. testoutput::

Steep the tea.

The single-quoted ``class`` and the unchanged text stay exactly as written; only the edited ```` tag rebuilds. That is the whole tree API. Head to the :doc:`/how-to/index` guides for task-focused recipes, the :doc:`/migration/index` guide if you are coming from another HTML library, or the :doc:`/reference` for the exact signatures.