20.2. html.parser— HTML および XHTML のシンプルなパーサー

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


このモジュールでは HTMLParser クラスを定義します。このクラスは HTML (ハイパーテキスト記述言語、HyperText Mark-up Language) および 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 になりました。

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

20.2.2. HTMLParser メソッド

HTMLParser インスタンスは以下のメソッドを提供します:

HTMLParser.feed(data)

パーサーにテキストを入力します。入力が完全なタグ要素で構成されている場合に限り処理が行われます; 不完全なデータであった場合、新たにデータが入力されるか、close() が呼び出されるまでバッファーされます。 datastr でなければなりません。

HTMLParser.close()

全てのバッファーされているデータについて、その後にファイル終端マークが続いているとみなして強制的に処理を行います。このメソッドは入力データの終端で行う追加処理を定義するために、派生クラスで再定義することができます。しかし、再定義されたバージョンでは、常に HTMLParser 基底クラスのメソッド close() を呼び出さなくてはなりません。

HTMLParser.reset()

インスタンスをリセットします。未処理のデータはすべて失われます。インスタンス化の際に暗黙的に呼び出されます。

HTMLParser.getpos()

現在の行番号およびオフセット値を返します。

HTMLParser.get_starttag_text()

最も最近開かれた開始タグのテキスト部分を返します。このテキストは必ずしも元データを構造化する上で必須ではありませんが、 “広く知られている (as deployed)” 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> aや <style>...</style> の内容)。

HTMLParser.handle_entityref(name)

このメソッドは &name; 形式の名前指定文字参照 (例: &gt;) を処理するために呼び出されます。name は一般実体参照になります (例: 'gt')。このメソッドは convert_charrefsTrue なら呼び出されることはありません。

HTMLParser.handle_charref(name)

このメソッドは &#NNN; あるいは &#xNNN; 形式の 10進および 16 進数値文字参照を処理するために呼び出されます。例えば、&gt; と等価な 10 進数は &#62; で、16進数は &#x3E; になります。この場合、メソッドは '62' あるいは 'x3E' を受け取ります。このメソッドは convert_charrefsTrue なら呼び出されることはありません。

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<![...]> マークアップ内の宣言の内容全体になります。 これは派生クラスで上書きする時に役立つことがあります。 基底クラスの実装では何もしません。

20.2.3. 使用例

以下のクラスは、より多くの例を示すのに用いられるパーサーの実装です:

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

それ以上のパースを行わずに、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.feed('&gt;&#62;&#x3E;')
Named ent: >
Num ent  : >
Num ent  : >

不完全なチャンクを feed() に入力しても、(convert_charrefsTrue に設定されていない限り) handle_data() は 1 回以上呼び出される場合があります:

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