Python HTMLParser 练习:链接、文本与属性
把解析器拆成小型处理器,更容易信任。这些练习每次只增加一个职责:收集标签、读取属性、捕获文本,最后组合成记录。
从标签收集器开始
重写 handle_starttag,把标签名加入列表。类要小到能在一屏内读完。只做一件事的解析器,比一次提取所有内容的解析器更容易测试。
from html.parser import HTMLParser
class Tags(HTMLParser):
def __init__(self):
super().__init__()
self.names = []
def handle_starttag(self, tag, attrs):
self.names.append(tag)
p = Tags()
p.feed("<article><h1>Title</h1><p>Text</p></article>")
print(p.names)一个处理器,一个职责
读取属性但不要崩溃
attrs 是键值对列表,有些值可能是 None。先转换成字典,再用 get。导航标记里缺少 href 很正常,应跳过,而不是把 None 加入结果。
from html.parser import HTMLParser
class Links(HTMLParser):
def __init__(self):
super().__init__()
self.links = []
def handle_starttag(self, tag, attrs):
values = dict(attrs)
if tag == "a" and values.get("href"):
self.links.append(values["href"])
p = Links()
p.feed('<a href="/one">One</a><a>No link</a><a href="/two">Two</a>')
print(p.links)跳过缺失值
捕获标签之间的文本
handle_data 接收的是一段段文本,不是完整句子。逐段去掉首尾空白,忽略空字符串,再决定如何连接。如果空白有意义,要有意识地保留,而不是依赖浏览器。
from html.parser import HTMLParser
class Text(HTMLParser):
def __init__(self):
super().__init__()
self.parts = []
def handle_data(self, data):
text = data.strip()
if text:
self.parts.append(text)
p = Text()
p.feed("<h1>Title</h1><p>First sentence.</p>")
print(" ".join(p.parts))清理后的文本节点
解码实体并保留来源
HTMLParser 会解码 & 这类字符引用。保存提取文本时,把原始 URL 和简短来源一起保留。解析器提供结构,来源信息让人可以核对结果。
from html import unescape
raw = "Tom & Jerry"
clean = unescape(raw)
print(clean)
print(raw)解码并保留原值
把多个处理器组合成记录
一个类同时收集链接和文本时,要显式管理状态,并为每个页面创建新实例。除非明确清空所有列表,否则不要跨文档复用解析器;残留状态是重复记录的常见来源。
from html.parser import HTMLParser
class Page(HTMLParser):
def __init__(self):
super().__init__()
self.links = []
self.text = []
def handle_starttag(self, tag, attrs):
href = dict(attrs).get("href")
if tag == "a" and href:
self.links.append(href)
def handle_data(self, data):
text = data.strip()
if text:
self.text.append(text)
p = Page()
p.feed('<h1>Docs</h1><a href="/docs">Read</a>')
print(p.links)
print(p.text)两个列表,一次遍历
你的练习清单
- 添加解析器状态前先调用 super().__init__。
- 用 dict(attrs).get(name),不要假设每个属性都存在。
- 清理文本节点并跳过空字符串。
- 每个文档创建新的解析器实例。
- 把来源 URL 和提取文本一起保存。
常见问题
HTMLParser 是完整浏览器吗?
不是。它是 Python 代码里的宽容分词器,不执行 JavaScript,也不构建完整 DOM。
为什么不用正则?
正则不理解嵌套和带引号的属性。解析器遵循文档结构,也更容易维护。
可以用 BeautifulSoup 吗?
可以,前提是能安装第三方包。标准库 HTMLParser 足以处理简单、结构良好的页面,而且没有依赖。
练习 HTML 解析
12 道题,覆盖标签、属性、文本节点、实体和解析器状态。
继续阅读: