html.parser
The html.parser library provides an HTML/XHTML parser compatible with Python’s html.parser module. Subclass HTMLParser and override its handler methods to react to start tags, end tags, text, comments, and other markup as the parser feeds through a document.
Import
import html.parser
HTMLParser = html.parser.HTMLParserAvailable Functions
| Function | Description |
|---|---|
HTMLParser(convert_charrefs=True) |
Construct a new parser instance |
feed(data) |
Feed HTML data to the parser |
reset() |
Reset the parser instance |
close() |
Force processing of buffered data |
get_starttag_text() |
Get the text of the most recently opened start tag |
getpos() |
Get the current (line, offset) position |
handle_starttag(tag, attrs) |
Handler called for a start tag (override) |
handle_endtag(tag) |
Handler called for an end tag (override) |
handle_startendtag(tag, attrs) |
Handler called for a self-closing tag (override) |
handle_data(data) |
Handler called for text content (override) |
handle_comment(data) |
Handler called for an HTML comment (override) |
handle_decl(decl) |
Handler called for a DOCTYPE declaration (override) |
handle_pi(data) |
Handler called for a processing instruction (override) |
handle_entityref(name) |
Handler called for a named character reference (override) |
handle_charref(name) |
Handler called for a numeric character reference (override) |
unknown_decl(data) |
Handler called for an unrecognized declaration (override) |
Constructing a Parser
HTMLParser(convert_charrefs=True)
Creates a new parser instance. Use a subclass and override the handle_* methods to process the elements you care about; feed() then drives those handlers as it parses.
Parameters:
convert_charrefs(bool, keyword-only, optional): IfTrue, character references (e.g.&) are automatically decoded before being passed tohandle_data, andhandle_entityref/handle_charrefare never called. Default:True.
Returns: HTMLParser: a new parser instance.
import html.parser
class MyHTMLParser(html.parser.HTMLParser):
def __init__(self):
self.data = []
def handle_starttag(self, tag, attrs):
print(f"Start tag: {tag}")
print(f"Attrs: {attrs}")
def handle_endtag(self, tag):
print(f"End tag: {tag}")
def handle_data(self, data):
self.data.append(data)
parser = MyHTMLParser()
parser.feed("<html><body><p>Hello World!</p></body></html>")Note:
super().__init__()is not required (or available): the parent class__init__is called automatically.
Feeding and Controlling the Parser
feed(data)
Feeds a chunk of HTML to the parser. The data is parsed immediately and the appropriate handle_* methods are invoked as elements are recognized.
Parsing follows Python 3.14’s html.parser, including how it recovers from malformed markup: an unclosed comment, a stray < or &, an empty comment such as <!--> and so on produce the same handler calls Python makes. An exception raised in a handler stops parsing and propagates out of feed().
Each call is parsed as a complete document: unlike Python, markup split across two feed() calls (for example feed("<a hr") then feed('ef="x">')) is not joined, so feed a whole document at once.
Parameters:
data(str): HTML markup to parse.
Returns: None
import html.parser
parser = html.parser.HTMLParser()
parser.feed("<h1>Title</h1><p>Paragraph</p>")reset()
Resets the parser to its initial state, clearing internal buffers and position tracking. Automatically called when the instance is created.
Parameters: None
Returns: None
import html.parser
parser = html.parser.HTMLParser()
parser.feed("<p>First document</p>")
parser.reset()
parser.feed("<p>Second document</p>")close()
Forces processing of any data still buffered. Call this once you’ve fed all available data. Since feed() parses each chunk completely, there is never buffered data, but calling close() keeps code portable to Python.
Parameters: None
Returns: None
import html.parser
parser = html.parser.HTMLParser()
parser.feed("<p>Some content</p>")
parser.close()get_starttag_text()
Returns the exact source text of the most recently opened start tag, including attributes as written (not normalized).
Parameters: None
Returns: str or None: the raw text of the last start tag, or None if no start tag has been seen yet.
import html.parser
class MyParser(html.parser.HTMLParser):
def handle_starttag(self, tag, attrs):
text = self.get_starttag_text()
print(f"Raw tag: {text}")
parser = MyParser()
parser.feed('<div id="main" class="content">')
# Raw tag: <div id="main" class="content">getpos()
Returns the parser’s current position in the source as a (line, offset) tuple. Line numbers start at 1. Position tracking is not updated during parsing; getpos() currently always returns (1, 0) after construction.
Parameters: None
Returns: tuple of int: (line, offset).
import html.parser
parser = html.parser.HTMLParser()
parser.feed("<p>Hello</p>")
pos = parser.getpos()
print(f"Line: {pos[0]}, Offset: {pos[1]}")Handler Methods
Override these methods in a subclass to react to the elements you care about. The default implementation of each does nothing (except handle_startendtag, which by default calls handle_starttag followed by handle_endtag).
handle_starttag(tag, attrs)
Called when a start tag is encountered, such as <div id="main">.
Parameters:
tag(str): Tag name, lowercased (e.g."div","p").attrs(list): List of(name, value)tuples for the tag’s attributes. Names are lowercased; character references in values are decoded, and an attribute written without a value (<input disabled>) has the valueNone.
Returns: None
def handle_starttag(self, tag, attrs):
print(f"<{tag}>")
for name, value in attrs:
print(f" {name}={value}")handle_endtag(tag)
Called when an end tag is encountered, such as </div>.
Parameters:
tag(str): Tag name, lowercased.
Returns: None
def handle_endtag(self, tag):
print(f"</{tag}>")handle_startendtag(tag, attrs)
Called for XHTML-style self-closing tags, such as <br/> or <img ... />. The default implementation calls handle_starttag followed by handle_endtag with the same arguments, so most subclasses don’t need to override this directly.
Parameters:
tag(str): Tag name, lowercased.attrs(list): List of(name, value)tuples for the tag’s attributes.
Returns: None
def handle_startendtag(self, tag, attrs):
print(f"<{tag}/>")handle_data(data)
Called with the text content found between tags.
Parameters:
data(str): Text content.
Returns: None
def handle_data(self, data):
if data.strip():
print(f"Text: {data}")handle_comment(data)
Called when an HTML comment is encountered. Malformed markup that HTML treats as a comment is reported here too, such as <!x> (content x) or </1> (content 1).
Parameters:
data(str): Comment content, without the surrounding<!--and-->.
Returns: None
def handle_comment(self, data):
print(f"Comment: {data}")handle_decl(decl)
Called for DOCTYPE and other markup declarations.
Parameters:
decl(str): Declaration content, without the surrounding<!and>.
Returns: None
def handle_decl(self, decl):
print(f"Declaration: {decl}")handle_pi(data)
Called for processing instructions, such as <?xml ...?>.
Parameters:
data(str): Processing instruction content: everything between<?and the first>, so<?xml version="1.0"?>givesxml version="1.0"?, as in Python.
Returns: None
def handle_pi(self, data):
print(f"PI: {data}")handle_entityref(name)
Called for named character references, such as >. Only invoked when convert_charrefs is False; otherwise these are decoded automatically and delivered through handle_data.
Parameters:
name(str): Entity name, without the surrounding∧.
Returns: None
def handle_entityref(self, name):
print(f"Entity: &{name};")handle_charref(name)
Called for numeric character references, such as >. Only invoked when convert_charrefs is False.
Parameters:
name(str): Character code, without the surrounding&#and;.
Returns: None
def handle_charref(self, name):
print(f"Char ref: &#{name};")unknown_decl(data)
Called for CDATA sections. The default implementation is a no-op.
Parameters:
data(str): Declaration content, from after<!to before the closing]]>:<![CDATA[x]]>givesCDATA[x.
Returns: None
Instance Attributes
convert_charrefs
Controls whether character references are automatically decoded.
Type: bool. Default: True.
When True, entities like & are converted to & before being passed to handle_data, and handle_entityref/handle_charref are not called. Set it to False before feeding data if you need to handle references yourself.
parser = html.parser.HTMLParser()
parser.convert_charrefs = FalseComplete Example
import html.parser
class LinkExtractor(html.parser.HTMLParser):
def __init__(self):
self.links = []
self.current_text = ""
self.in_link = False
def handle_starttag(self, tag, attrs):
if tag == "a":
self.in_link = True
for name, value in attrs:
if name == "href":
self.links.append({"href": value, "text": ""})
def handle_endtag(self, tag):
if tag == "a":
if self.links and self.in_link:
self.links[-1]["text"] = self.current_text.strip()
self.current_text = ""
self.in_link = False
def handle_data(self, data):
if self.in_link:
self.current_text = self.current_text + data
# Extract links from HTML
parser = LinkExtractor()
parser.feed("""
<html>
<body>
<a href="https://example.com">Example</a>
<a href="https://google.com">Google</a>
</body>
</html>
""")
for link in parser.links:
print(f"{link['text']}: {link['href']}")
# Output:
# Example: https://example.com
# Google: https://google.comPython Compatibility
super().__init__()is not required (or available): the parent class__init__is called automatically.- Each
feed()is parsed as a complete document; Python buffers a construct left incomplete at the end of onefeed()until the next. - The content of
script,styleand the other raw-text elements (xmp,iframe,noembed,noframes,plaintext,textarea,title) is parsed as markup rather than passed tohandle_dataas text. getpos()always returns(1, 0).