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 道題,覆蓋標籤、屬性、文位元組點、實體和解析器狀態。
繼續閱讀: