html.parser --- HTML と XHTML のシンプルなパーサー

ソースコード: Lib/html/parser.py


このモジュールでは HTMLParser クラスを定義します。このクラスは HTML (ハイパーテキスト記述言語、HyperText Mark-up Language) および XHTML で書式化されているテキストファイルを解釈するための基礎となります。

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

不正なマークアップをパースできるパーサーインスタンスを作成します。

convert_charrefs が真 (デフォルト) の場合、script や style などの要素内を除くすべての文字参照を、対応する Unicode 文字に自動で変換します。

scripting が偽 (デフォルト) の場合、noscript 要素の内容を通常どおり解析します。 真の場合は解析せずにそのまま返します。

HTMLParser インスタンスに HTML データを渡すと、開始タグ、終了タグ、テキスト、コメント、その他のマークアップ要素を見つけるたびにハンドラーメソッドを呼び出します。 利用者は HTMLParser のサブクラスを作り、メソッドをオーバーライドして必要な動作を実装します。

このパーサーは終了タグが開始タグと一致しているか調べたり、外側のタグ要素が閉じるときに内側で明示的に閉じられていないタグ要素のタグ終了ハンドラーを呼び出したりはしません。

バージョン 3.4 で変更: キーワード引数 convert_charrefs を追加。

バージョン 3.5 で変更: convert_charrefs のデフォルト値は True になりました。

バージョン 3.14.1 で変更: scripting 引数を追加しました。

HTML パーサーアプリケーションの例

基本的な例として、以下は HTMLParser クラスを使い、開始タグ、終了タグ、データを見つけるたびに表示するシンプルな HTML パーサーです。

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 を囲む引用符は削除され、文字参照と実体参照は置換されています。 値のない属性では value は None です。

例えば、タグ <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、style などの要素の内容) を処理するために呼び出されます。

HTMLParser.handle_entityref(name)

このメソッドは、&name; 形式の名前付き文字参照 (&gt; など) を処理するために呼び出されます。 name は一般実体参照 ('gt' など) です。 このメソッドは convert_charrefs が偽の場合にだけ呼び出されます。

HTMLParser.handle_charref(name)

このメソッドは、&#NNN; 形式と &#xNNN; 形式の、それぞれ 10 進数と 16 進数による数値文字参照を処理するために呼び出されます。 例えば &gt; に対応する表記は、10 進数では &#62;、16 進数では &#x3E; です。 この場合、メソッドは '62' または 'x3E' を受け取ります。 このメソッドは convert_charrefs が偽の場合にだけ呼び出されます。

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 宣言を扱うために呼び出されます (例: <!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()

doctype の解析:

>>> 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

script や style などの要素の内容は、さらに解析せずにそのまま返します。

>>> 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! &#9786;</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello! &#9786;</strong>");
End tag  : script

属性名は小文字に変換され、属性値を囲む引用符は削除されます。 (checked のような) 値のない属性では value として None が返されます。

>>> parser.feed("<input TYPE='checkbox' checked required='' disabled=disabled>")
Start tag: input
     attr: ('type', 'checkbox')
     attr: ('checked', None)
     attr: ('required', '')
     attr: ('disabled', 'disabled')

コメントの解析:

>>> 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() に渡すこともできますが、convert_charrefs が偽の場合は handle_data() が複数回呼び出されることがあります。

>>> 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