Inspect a node you already hold¶
Work with a node in hand rather than search beneath it: read its text and
html, dispatch on its kind with a match statement, and ask it for a CSS or XPath locator
back to the document root.
Read the text or markup of a node¶
text is the concatenated character data of a node’s subtree, with references decoded;
html re-serializes the subtree back to HTML (attributes quoted, specials escaped):
article = turbohtml.parse("<article><h1>Title</h1><p>Tom & Jerry</p></article>").find("article")
print(article.text)
print(article.find("p").html)
TitleTom & Jerry
<p>Tom & Jerry</p>
text gathers every text node in the subtree, including the contents of script and style elements when they
sit inside it; filter those out by walking descendants yourself when you need only rendered
text.
Match nodes with structural patterns¶
The node types are a sealed hierarchy with __match_args__ set, so a match statement dispatches on
node kind and unpacks the defining field (tag for an Element, data for a
Text or Comment):
def summarize(node: turbohtml.Node) -> str:
match node:
case turbohtml.Element(tag):
return f"<{tag}>"
case turbohtml.Text(data):
return repr(data)
case turbohtml.Comment(data):
return f"<!--{data}-->"
case _:
return "?"
print([summarize(child) for child in turbohtml.parse("<p>hi<!--x--><b>bold</b></p>").find("p")])
["'hi'", '<!--x-->', '<b>']
Get the path to a node¶
Once you have an element, ask it for a locator back to the document root, the way browser devtools “copy selector” and
lxml’s getpath do. css_path() returns a CSS selector and
xpath_path() returns a positional XPath; both round-trip, so feeding the result to
select() or xpath() on the document returns exactly that element. Use them
to log a match, store a stable reference, or debug a scrape:
import turbohtml
doc = turbohtml.parse("<body><div><p>one</p><p>two</p></div></body>")
second = doc.select("p")[1]
print(second.css_path())
print(second.xpath_path())
print(doc.select(second.css_path()) == [second])
html > body > div > p:nth-of-type(2)
/html/body/div/p[2]
True
The CSS path anchors at the nearest ancestor (or the element itself) carrying a document-unique id, which keeps it
short and stable against reordering; otherwise it descends from the root with :nth-of-type() steps. The XPath form
is always positional, like getpath:
doc = turbohtml.parse('<body><main id="content"><ul><li>a</li><li>b</li></ul></main></body>')
item = doc.select("li")[1]
print(item.css_path())
print(item.xpath_path())
#content > ul > li:nth-of-type(2)
/html/body/main/ul/li[2]