html.parser 是 Python 标准库中的一个模块,它提供了一个简单的、基于事件的 HTML 解析器,这意味着它不会一次性将整个 HTML 文档加载到内存中并转换成一个复杂的树结构(像 lxml 或 BeautifulSoup 那样),而是逐个字符地扫描 HTML 文档,当遇到特定的标签、数据或事件时,会触发你预先定义好的处理函数。

为什么选择 html.parser?
- 无需安装:它是 Python 标准库的一部分,
import html即可使用,无需像BeautifulSoup或lxml那样通过pip安装。 - 轻量级:对于简单的解析任务,它非常轻量,没有额外的依赖。
- 易于理解:基于事件驱动,逻辑清晰,适合学习 HTML 解析的基本原理。
html.parser 的工作原理:事件驱动
HTMLParser 的工作核心是事件,当你解析一段 HTML 文本时,解析器会根据它遇到的内容,调用不同的方法,你需要创建一个 HTMLParser 的子类,并重写这些方法来处理你感兴趣的事件。
主要的事件方法包括:
| 事件方法 | 触发时机 |
|---|---|
handle_starttag(tag, attrs) |
遇到开始标签,如 <div> 或 <a href='...'> |
handle_endtag(tag) |
遇到结束标签,如 </div> |
handle_startendtag(tag, attrs) |
遇到自闭合标签,如 <img src='...'> 或 <br/> |
handle_data(data) |
遇到标签内的文本数据 |
handle_comment(data) |
遇到注释 <!-- ... --> |
handle_decl(decl) |
遇到文档类型声明 <!DOCTYPE ...> |
实战演练:一个简单的例子
让我们从一个最简单的例子开始,目标是解析一段 HTML 并打印出所有标签和它们内部的文本。
目标 HTML:

<html>
<head>一个简单的网页</title>
</head>
<body>
<h1>欢迎!</h1>
<p>这是一个段落。</p>
<p>这是<b>另一个</b>段落。</p>
</body>
</html>
Python 代码:
from html.parser import HTMLParser
# 1. 创建一个自定义的解析器类,继承自 HTMLParser
class MyHTMLParser(HTMLParser):
# 2. 重写 handle_starttag 方法
def handle_starttag(self, tag, attrs):
print(f"遇到开始标签: <{tag}>")
# attrs 是一个 (name, value) 元组的列表
if attrs:
print(f" 属性: {attrs}")
# 3. 重写 handle_endtag 方法
def handle_endtag(self, tag):
print(f"遇到结束标签: </{tag}>")
# 4. 重写 handle_data 方法
def handle_data(self, data):
# data 可能包含很多空白字符,我们只处理非空的
if data.strip():
print(f"遇到数据: '{data}'")
# --- 主程序 ---
if __name__ == "__main__":
html_string = """
<html>
<head>
<title>一个简单的网页</title>
</head>
<body>
<h1>欢迎!</h1>
<p>这是一个段落。</p>
<p>这是<b>另一个</b>段落。</p>
</body>
</html>
"""
# 5. 创建解析器实例
parser = MyHTMLParser()
# 6. 使用 feed() 方法喂入 HTML 字符串进行解析
parser.feed(html_string)
输出结果:
遇到开始标签: <html>
遇到开始标签: <head>
遇到开始标签: <title>
遇到数据: '一个简单的网页'
遇到结束标签: </title>
遇到结束标签: </head>
遇到开始标签: <body>
遇到开始标签: <h1>
遇到数据: '欢迎!'
遇到结束标签: </h1>
遇到开始标签: <p>
遇到数据: '这是一个段落。'
遇到结束标签: </p>
遇到开始标签: <p>
遇到数据: '这是'
遇到开始标签: <b>
遇到数据: '另一个'
遇到结束标签: </b>
遇到数据: '段落。'
遇到结束标签: </p>
遇到结束标签: </body>
遇到结束标签: </html>
从这个输出中,你可以清晰地看到解析器是如何一步步处理 HTML 的。
更复杂的例子:提取所有链接
现在我们来一个更实用的例子:从一个 HTML 页面中提取出所有 <a> 标签的 href 属性。

目标 HTML:
<html>
<body>
<h1>我的网站</h1>
<p>请访问我的<a href="https://www.example.com">主页</a>。</p>
<p>或者看看这个<a class="external" href="https://www.python.org">Python官网</a>。</p>
<img src="image.png" />
</body>
</html>
Python 代码:
from html.parser import HTMLParser
class LinkParser(HTMLParser):
def __init__(self):
super().__init__()
self.links = [] # 用来存储找到的链接
def handle_starttag(self, tag, attrs):
# 我们只关心 <a> 标签
if tag == 'a':
# attrs 是一个列表,我们可以把它转换成字典方便查找
attributes = dict(attrs)
# 检查是否存在 'href' 属性
if 'href' in attributes:
self.links.append(attributes['href'])
def get_links(self):
return self.links
# --- 主程序 ---
if __name__ == "__main__":
html_string = """
<html>
<body>
<h1>我的网站</h1>
<p>请访问我的<a href="https://www.example.com">主页</a>。</p>
<p>或者看看这个<a class="external" href="https://www.python.org">Python官网</a>。</p>
<img src="image.png" />
</body>
</html>
"""
parser = LinkParser()
parser.feed(html_string)
print("找到的链接:")
for link in parser.get_links():
print(link)
输出结果:
找到的链接:
https://www.example.com
https://www.python.org
html.parser 的局限性
虽然 html.parser 很方便,但它也有一些明显的局限性,这也是为什么在实际项目中,人们更倾向于使用 BeautifulSoup 或 lxml 的原因:
- 容错性差:
html.parser对格式错误的 HTML 非常敏感,HTML 代码有哪怕一点点语法错误(比如标签未闭合),解析器可能会抛出异常或直接停止解析。 - 功能有限:它只提供基础的解析功能,无法方便地进行复杂的导航,找到第三个
<div>下的所有<span>”或“根据 CSS 类名查找元素”,这些操作在BeautifulSoup中非常简单。 - 性能一般:相比于用 C 语言编写的
lxml,纯 Python 实现的html.parser性能要慢一些。
html.parser 与 BeautifulSoup 的关系
BeautifulSoup 实际上是一个“包装器”或“适配器”,它可以使用不同的解析器作为其后端,其中就包括 Python 内置的 html.parser。
当你这样使用 BeautifulSoup 时:
from bs4 import BeautifulSoup soup = BeautifulSoup(html_string, 'html.parser')
BeautifulSoup 会帮你处理所有复杂的解析工作,并提供一个简单、强大的 API 来遍历和搜索 HTML 文档。html.parser 解析失败,你甚至可以轻松切换到更强大的 lxml:
soup = BeautifulSoup(html_string, 'lxml') # 只需改一个参数
| 特性 | html.parser |
BeautifulSoup / lxml |
|---|---|---|
| 安装 | Python 标准库,无需安装 | 需要通过 pip 安装 |
| 易用性 | 较低,需要手动处理事件 | 非常高,提供简洁的 API |
| 容错性 | 较差,对错误 HTML 敏感 | 非常好,能修复和解析不规范的 HTML |
| 功能 | 基础,仅解析功能 | 强大,支持 CSS 选择器、XPath、修改文档等 |
| 性能 | 一般 | lxml 很快,BeautifulSoup (用 lxml 作后端) 也很快 |
如何选择?
- 如果你只需要快速写一个简单的脚本,处理一小段格式良好的 HTML,并且不想安装任何第三方库,
html.parser是一个不错的选择。 - 如果你在进行任何严肃的 Web 爬虫或数据处理项目,或者需要处理来源不明的、可能格式混乱的 HTML,强烈推荐使用
BeautifulSoup并搭配lxml解析器,它能让你事半功倍,代码也更健壮、更易读。
