Walk a tree with a cursor and filter¶
Drive a turbohtml.TreeWalker or turbohtml.NodeIterator over a parsed tree, selecting node types with a
what_to_show bitmask and refining the walk with a turbohtml.NodeFilter callback.
Steer a cursor with TreeWalker¶
A TreeWalker is a movable cursor. Build it from the root node, then step in any direction; each move
sets current_node to the node it lands on and returns it, or returns None (leaving the
cursor put) when there is nothing that way. what_to_show restricts which node types count – here elements only:
import turbohtml
from turbohtml import NodeFilter, TreeWalker
doc = turbohtml.parse("<ul><li>one</li><li>two</li></ul>")
walker = TreeWalker(doc.find("ul"), NodeFilter.SHOW_ELEMENT)
print(walker.first_child())
print(walker.next_sibling())
print(walker.parent_node())
Element('li')
Element('li')
Element('ul')
Select node types with what_to_show¶
what_to_show is a bitmask of NodeFilter SHOW_* constants; OR them to keep several kinds, or
use SHOW_ALL. A node whose type is not in the mask is skipped before the filter ever runs:
doc = turbohtml.parse("<p>Alpha<!-- note -->Beta<b>Gamma</b></p>")
text_only = TreeWalker(doc.find("p"), NodeFilter.SHOW_TEXT)
print([node.data for node in iter(text_only.next_node, None)])
['Alpha', 'Beta', 'Gamma']
Prune subtrees with a NodeFilter¶
Pass a filter callable to decide each node. Return FILTER_ACCEPT to keep it,
FILTER_SKIP to drop the node but keep walking its children, or
FILTER_REJECT to drop the node and its whole subtree. Rejecting a <nav> removes its
links; skipping it would leave them:
page = turbohtml.parse("<body><nav><a href='/'>home</a></nav><main><a href='/post'>post</a></main></body>")
def without_nav(node):
if node.tag == "nav":
return NodeFilter.FILTER_REJECT
return NodeFilter.FILTER_ACCEPT
walker = TreeWalker(page.find("body"), NodeFilter.SHOW_ELEMENT, without_nav)
print([node.tag for node in iter(walker.next_node, None)])
['main', 'a']
Take a flat view with NodeIterator¶
When you want the accepted nodes as a plain forward (or backward) sequence rather than a steerable cursor, use
turbohtml.NodeIterator. It iterates directly, and its flat view has no subtree to prune, so FILTER_REJECT
and FILTER_SKIP behave the same:
from turbohtml import NodeIterator
doc = turbohtml.parse("<article><h2>Q3</h2><p>Up 12%</p></article>")
iterator = NodeIterator(doc.find("article"), NodeFilter.SHOW_ELEMENT)
print([node.tag for node in iterator])
['article', 'h2', 'p']
Move backward with previous_node(); reference_node reports
where the iterator currently sits.