CodeRigor

FREE LESSON

HTML documents, elements & parser events

Separate HTML source, the document tree and streaming parser events while counting elements and retaining decoded text.

HTML describes a document with elements, attributes and text. In <p class="note">Hi <em>Sam</em></p>, the paragraph contains a text node and an emphasis element. The class attribute belongs to the paragraph; it is not a child element. A browser builds a document object model (DOM) and applies rendering rules. The text file and the resulting tree are related representations, not interchangeable objects.

A document has structure
start / 2
The paragraph owns an attribute and two children; the emphasis element owns the text Sam.
A document has structure
The paragraph owns an attribute and two children; the emphasis element owns the text Sam.

A normal element often has a start tag, content and an end tag. A void element such as img or br has no end tag in HTML. A trailing slash on <div/> does not make a div a void element in browser HTML. Python’s HTMLParser nevertheless reports that lexical form through handle_startendtag. Our inventory counts source start events; it does not certify conformance or reconstruct the browser’s repaired DOM.

Subclassing gives the parser a small set of callbacks. class Trace(HTMLParser) reuses the standard parser. self is the particular instance receiving an event, and super().__init__() initializes its inherited machinery. Override only the handlers needed for the result. A fresh instance keeps one document’s counters from leaking into another. This is the limited class pattern used here; a larger class-design course is optional preparation for larger projects.

Python
from html.parser import HTMLParser

class Trace(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("start", tag)
    def handle_data(self, data):
        print("data", repr(data))

parser = Trace(convert_charrefs=True)
parser.feed('<P>Fish &amp; chips<br></P>')
parser.close()

Names passed to the start handler are lowercase. Data contains decoded character references such as &amp;. Attribute values are also decoded. Comments use their own handler, so <!-- <a> --> must not add an anchor to the inventory. A doctype is a declaration, not an element. Splitting the input into multiple feed calls can split text callbacks differently, so collect chunks and join them instead of treating a callback as a whole paragraph.

Callbacks produce separate evidence
start / 3
Start events update element counts, comments update a counter, and data chunks join into source text.
Callbacks produce separate evidence
Start events update element counts, comments update a counter, and data chunks join into source text.

close() processes buffered input at end of source. It does not invent missing end tags, validate nesting or execute scripts. Script and style contents can arrive as data; they are source text, not evidence of what a visitor sees. The build intentionally retains every data chunk, including whitespace and raw-text content, and ignores end-tag events when counting elements.

Implement describe_html(source) with three independent outputs: a mapping of element-name counts, a comment count and concatenated data. Test an empty document, uppercase names, a void tag, an XHTML-style empty tag and a comment containing apparent markup. Before coding, predict which callbacks each fragment triggers. Use structured parser events for HTML; a regular expression searching for angle brackets cannot reliably handle quoted attributes, comments, nesting and raw-text elements.

The result is an offline source description. It makes no requests, checks no links and claims no accessibility, browser-layout or HTML-validity audit. Those limits make its small contract useful and testable.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Watch 03:30–10:30 for HTML structure and the DOM, then 17:15–23:50 for images and links. The video supplies browser context; this lesson separately teaches Python’s source-event parser.