html.parser --- 簡單的 HTML 和 XHTML 剖析器

原始碼:Lib/html/parser.py


該模組定義了一個類別HTMLParser,是剖析 (parse) HTML(HyperText Mark-up Language、超文本標記語言)和 XHTML 格式文本檔案的基礎。

classhtml.parser.HTMLParser(*,convert_charrefs=True)

建立一個能夠剖析無效標記的剖析器實例。

如果convert_charrefsTrue (預設值),所有字元參照 (reference)(script/style 元素中的參照除外)將自動轉換為相應的 Unicode 字元。

HTMLParser 實例被提供 HTML 資料,並在遇到開始標籤、結束標籤、文本、註解和其他標記元素時呼叫處理程式 (handler) 方法。使用者應該繼承HTMLParser 並覆蓋其方法以實作所需的行為。

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

在 3.4 版的變更:新增關鍵字引數convert_charrefs

在 3.5 版的變更:引數convert_charrefs 的預設值現在是True

HTML 剖析器應用程式範例

以下的基礎範例是一個簡單的 HTML 剖析器,它使用HTMLParser 類別,當遇到開始標籤、結束標籤和資料時將它們印出:

fromhtml.parserimportHTMLParserclassMyHTMLParser(HTMLParser):defhandle_starttag(self,tag,attrs):print("Encountered a start tag:",tag)defhandle_endtag(self,tag):print("Encountered an end tag :",tag)defhandle_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: htmlEncountered a start tag: headEncountered a start tag: titleEncountered some data  : TestEncountered an end tag : titleEncountered an end tag : headEncountered a start tag: bodyEncountered a start tag: h1Encountered some data  : Parse me!Encountered an end tag : h1Encountered an end tag : bodyEncountered an end tag : html

HTMLParser 方法

HTMLParser 實例具有以下方法:

HTMLParser.feed(data)

向剖析器提供一些文本。只要它由完整的元素組成,它就會被處理;不完整的資料會被緩衝,直到輸入更多資料或呼叫close()data 必須是str

HTMLParser.close()

強制處理所有緩衝資料,如同它後面跟有文件結束標籤一樣。此方法可能有被衍生類別重新定義,以在輸入末尾定義額外的處理,但重新定義的版本仍應要呼叫HTMLParser 基底類別方法close()

HTMLParser.reset()

重置實例。丟棄所有未處理的資料。這在實例化時被會隱晦地呼叫。

HTMLParser.getpos()

回傳目前列號 (line number) 和偏移量 (offset)。

HTMLParser.get_starttag_text()

回傳最近開啟 (open) 的開始標籤的文本。這對於結構化處理通常不必要,但在處理「已部署」的 HTML 或以最少的更改重新生成輸入(可以保留屬性之間的空白等)時可能很有用。

當遇到資料或標記元素時將呼叫以下方法,並且它們應在子類別中被覆蓋。基底類別實作什麼都不做(除了handle_startendtag()):

HTMLParser.handle_starttag(tag,attrs)

呼叫此方法來處理元素的開始標籤(例如<divid="main">)。

tag 引數是轉換為小寫的標籤名稱。attrs 引數是一個(name,value) 對的列表,包含在標籤的<> 括號內找到的屬性。name 將被轉成小寫,value 中的引號會被刪除,字元和實體參照也會被替換。

例如,對於標籤<AHREF="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.../>) 時呼叫。這個方法可能被需要這個特定詞彙資訊 (lexical information) 的子類別覆蓋;預設實作只是呼叫handle_starttag()handle_endtag()

HTMLParser.handle_data(data)

呼叫此方法來處理任意資料(例如文本節點與<script>...</script><style>...</style> 的內容)。

HTMLParser.handle_entityref(name)

呼叫此方法來處理形式為&name; (例如&gt;)的附名字元參照,其中name 是一般實體參照(例如'gt')。如果convert_charrefsTrue,則永遠不會呼叫此方法。

HTMLParser.handle_charref(name)

呼叫此方法來處理&#NNN;&#xNNN; 形式的十進位和十六進位數字字元參照。例如,&gt; 的十進位等效為&#62;,而十六進位為&#x3E;;在這種情況下,該方法將收到'62''x3E'。如果convert_charrefsTrue,則永遠不會呼叫此方法。

HTMLParser.handle_comment(data)

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

舉例來說,註解<!--comment--> 會使得此方法被以引數'comment' 來呼叫。

Internet Explorer 條件式註解(conditional comments, condcoms)的內容也會被發送到這個方法,故以<!--[ifIE9]>IE9-specificcontent<![endif]--> 為例,這個方法將會收到'[ifIE9]>IE9-specificcontent<![endif]'

HTMLParser.handle_decl(decl)

呼叫此方法來處理 HTML 文件類型聲明 (doctype declaration)(例如<!DOCTYPEhtml>)。

decl 參數將是<!...> 標記內聲明部分的全部內容(例如'DOCTYPEhtml')。

HTMLParser.handle_pi(data)

遇到處理指示 (processing instruction) 時會呼叫的方法。data 參數將包含整個處理指示。例如,對於處理指示<?proccolor='red'>,這個方法將以handle_pi("proccolor='red'") 形式被呼叫。它旨在被衍生類別覆蓋;基底類別實作中什麼都不做。

備註

HTMLParser 類別使用 SGML 語法規則來處理指示。使用有? 跟隨在後面的 XHTML 處理指示將導致? 被包含在data 中。

HTMLParser.unknown_decl(data)

當剖析器讀取無法識別的聲明時會呼叫此方法。

data 參數將是<![...]> 標記內聲明的全部內容。有時被衍生類別被覆蓋會是好用的。在基底類別實作中什麼都不做。

範例

以下類別實作了一個剖析器,將用於解說更多範例:

fromhtml.parserimportHTMLParserfromhtml.entitiesimportname2codepointclassMyHTMLParser(HTMLParser):defhandle_starttag(self,tag,attrs):print("Start tag:",tag)forattrinattrs:print("     attr:",attr)defhandle_endtag(self,tag):print("End tag  :",tag)defhandle_data(self,data):print("Data     :",data)defhandle_comment(self,data):print("Comment  :",data)defhandle_entityref(self,name):c=chr(name2codepoint[name])print("Named ent:",c)defhandle_charref(self,name):ifname.startswith('x'):c=chr(int(name[1:],16))else:c=chr(int(name))print("Num ent  :",c)defhandle_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: h1Data     : PythonEnd 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 commentComment  : [if IE 9]>IE-specific content<![endif]

剖析附名 (named) 且為數值的 (numeric) 字元參照,並將它們轉換為正確的字元(注意:這 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):

>>>forchunkin['<sp','an>buff','ered',' text</s','pan>']:...parser.feed(chunk)...Start tag: spanData     : buffData     : eredData     :  textEnd tag  : span

也能夠剖析無效的 HTML(例如未加引號的屬性):

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