html.parser — 簡單的 HTML 和 XHTML 解析器

原始碼: Lib/html/parser.py


此模組定義了一個類 HTMLParser,作為解析 HTML (超文字標記語言) 和 XHTML 格式文字檔案的基礎。

class html.parser.HTMLParser(*, convert_charrefs=True)

建立一個能夠解析無效標記的解析器例項。

如果 *convert_charrefs* 為 True(預設值),則所有字元引用(script / style 元素中的除外)都會自動轉換為對應的 Unicode 字元。

當遇到開始標籤、結束標籤、文字、註釋和其他標記元素時,HTMLParser 例項會接收 HTML 資料並呼叫處理方法。使用者應子類化 HTMLParser 並重寫其方法以實現所需行為。

此解析器不會檢查結束標籤是否與開始標籤匹配,也不會為透過隱式關閉外部元素而關閉的元素呼叫結束標籤處理程式。

版本 3.4 中的新功能: 添加了 *convert_charrefs* 關鍵字引數。

版本 3.5 中的新功能: 引數 *convert_charrefs* 的預設值現在是 True

HTML 解析器應用程式示例

作為一個基本示例,下面是一個簡單的 HTML 解析器,它使用 HTMLParser 類來列印遇到的開始標籤、結束標籤和資料。

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Encountered a start tag:", tag)

    def handle_endtag(self, tag):
        print("Encountered an end tag :", tag)

    def handle_data(self, data):
        print("Encountered some data  :", data)

parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
            '<body><h1>Parse me!</h1></body></html>')

輸出將是

Encountered a start tag: html
Encountered a start tag: head
Encountered a start tag: title
Encountered some data  : Test
Encountered an end tag : title
Encountered an end tag : head
Encountered a start tag: body
Encountered a start tag: h1
Encountered some data  : Parse me!
Encountered an end tag : h1
Encountered an end tag : body
Encountered an end tag : html

HTMLParser 方法

HTMLParser 例項具有以下方法

HTMLParser.feed(data)

向解析器提供一些文字。它會處理其中完整的元素;不完整的資料會被緩衝,直到提供更多資料或呼叫 close()。*data* 必須是 str

HTMLParser.close()

強制處理所有緩衝資料,就好像它後面跟著一個檔案結束標記一樣。此方法可以由派生類重新定義,以在輸入結束時定義額外的處理,但重新定義的版本應始終呼叫 HTMLParser 基類方法 close()

HTMLParser.reset()

重置例項。丟失所有未處理的資料。這在例項化時會隱式呼叫。

HTMLParser.getpos()

返回當前行號和偏移量。

HTMLParser.get_starttag_text()

返回最近開啟的開始標籤的文字。對於結構化處理,通常不需要此功能,但在處理“已部署”的 HTML 或以最小更改(可以保留屬性之間的空格等)重新生成輸入時可能會有用。

當遇到資料或標記元素時,會呼叫以下方法,它們旨在在子類中被重寫。基類實現什麼也不做(handle_startendtag() 除外)

HTMLParser.handle_starttag(tag, attrs)

此方法用於處理元素的開始標籤(例如 <div id="main">)。

*tag* 引數是轉換為小寫的標籤名稱。*attrs* 引數是 (name, value) 對的列表,包含在標籤的 <> 括號中找到的屬性。*name* 將轉換為小寫,*value* 中的引號已被移除,字元和實體引用已被替換。

例如,對於標籤 <A HREF="https://www.cwi.nl/">,此方法將作為 handle_starttag('a', [('href', 'https://www.cwi.nl/')]) 被呼叫。

html.entities 中的所有實體引用都在屬性值中被替換。

HTMLParser.handle_endtag(tag)

此方法用於處理元素的結束標籤(例如 </div>)。

*tag* 引數是轉換為小寫的標籤名稱。

HTMLParser.handle_startendtag(tag, attrs)

類似於 handle_starttag(),但當解析器遇到 XHTML 風格的空標籤(<img ... />)時呼叫。此方法可以由需要此特定詞法資訊的子類重寫;預設實現只是呼叫 handle_starttag()handle_endtag()

HTMLParser.handle_data(data)

此方法用於處理任意資料(例如文字節點以及 <script>...</script><style>...</style> 的內容)。

HTMLParser.handle_entityref(name)

此方法用於處理 &name; 形式的命名字元引用(例如 &gt;),其中 *name* 是一個通用實體引用(例如 'gt')。如果 *convert_charrefs* 為 True,則此方法永遠不會被呼叫。

HTMLParser.handle_charref(name)

此方法用於處理 &#NNN;&#xNNN; 形式的十進位制和十六進位制數字字元引用。例如,&gt; 的十進位制等價物是 &#62;,而十六進位制是 &#x3E;;在這種情況下,該方法將接收 '62''x3E'。如果 *convert_charrefs* 為 True,則此方法永遠不會被呼叫。

HTMLParser.handle_comment(data)

當遇到註釋時呼叫此方法(例如 <!--comment-->)。

例如,註釋 <!-- comment --> 將導致此方法以引數 ' comment ' 被呼叫。

Internet Explorer 條件註釋 (condcoms) 的內容也將傳送到此方法,因此,對於 <!--[if IE 9]>IE9-specific content<![endif]-->,此方法將接收 '[if IE 9]>IE9-specific content<![endif]'

HTMLParser.handle_decl(decl)

此方法用於處理 HTML 文件型別宣告(例如 <!DOCTYPE html>)。

*decl* 引數將是 <!...> 標記內宣告的整個內容(例如 'DOCTYPE html')。

HTMLParser.handle_pi(data)

遇到處理指令時呼叫的方法。*data* 引數將包含整個處理指令。例如,對於處理指令 <?proc color='red'>,此方法將作為 handle_pi("proc color='red'") 被呼叫。它旨在由派生類重寫;基類實現什麼也不做。

備註

HTMLParser 類使用 SGML 語法規則處理處理指令。使用尾隨 '?' 的 XHTML 處理指令將導致 '?' 包含在 *data* 中。

HTMLParser.unknown_decl(data)

當解析器讀取到無法識別的宣告時呼叫此方法。

*data* 引數將是 <![...]> 標記內宣告的整個內容。有時由派生類重寫很有用。基類實現什麼也不做。

示例

以下類實現了一個解析器,將用於說明更多示例

from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Start tag:", tag)
        for attr in attrs:
            print("     attr:", attr)

    def handle_endtag(self, tag):
        print("End tag  :", tag)

    def handle_data(self, data):
        print("Data     :", data)

    def handle_comment(self, data):
        print("Comment  :", data)

    def handle_entityref(self, name):
        c = chr(name2codepoint[name])
        print("Named ent:", c)

    def handle_charref(self, name):
        if name.startswith('x'):
            c = chr(int(name[1:], 16))
        else:
            c = chr(int(name))
        print("Num ent  :", c)

    def handle_decl(self, data):
        print("Decl     :", data)

parser = MyHTMLParser()

解析文件型別

>>> parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
...             '"http://www.w3.org/TR/html4/strict.dtd">')
Decl     : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"

解析帶有一些屬性和標題的元素

>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
Start tag: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'The Python logo')
>>>
>>> parser.feed('<h1>Python</h1>')
Start tag: h1
Data     : Python
End tag  : h1

scriptstyle 元素的內容按原樣返回,不進行進一步解析

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Start tag: style
     attr: ('type', 'text/css')
Data     : #python { color: green }
End tag  : style

>>> parser.feed('<script type="text/javascript">'
...             'alert("<strong>hello!</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello!</strong>");
End tag  : script

解析註釋

>>> parser.feed('<!--a comment-->'
...             '<!--[if IE 9]>IE-specific content<![endif]-->')
Comment  : a comment
Comment  : [if IE 9]>IE-specific content<![endif]

解析命名和數字字元引用並將它們轉換為正確的字元(注意:這 3 個引用都等同於 '>'

>>> parser = MyHTMLParser()
>>> parser.feed('&gt;&#62;&#x3E;')
Data     : >>>

>>> parser = MyHTMLParser(convert_charrefs=False)
>>> parser.feed('&gt;&#62;&#x3E;')
Named ent: >
Num ent  : >
Num ent  : >

feed() 提供不完整的塊是有效的,但 handle_data() 可能會被多次呼叫(除非 *convert_charrefs* 設定為 True

>>> for chunk in ['<sp', 'an>buff', 'ered', ' text</s', 'pan>']:
...     parser.feed(chunk)
...
Start tag: span
Data     : buff
Data     : ered
Data     :  text
End tag  : span

解析無效 HTML(例如未加引號的屬性)也有效

>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
Start tag: p
Start tag: a
     attr: ('class', 'link')
     attr: ('href', '#main')
Data     : tag soup
End tag  : p
End tag  : a