# 19.2. `sgmllib` --- 単純な SGML パーザ

バージョン 2.6 で非推奨: `sgmllib` モジュールは Python 3 で削除されました。

このモジュールでは SGML (Standard Generalized Mark-up Language: 汎用マークアップ言語標準) で書式化されたテキストファイルを解析するための基礎として働く `SGMLParser` クラスを定義しています。実際には、このクラスは完全な SGML パーザを提供しているわけではありません --- このクラスは HTML で用いられているような SGML だけを解析し、モジュール自体も `htmllib` モジュールの基礎にするためだけに存在しています。XHTML をサポートし、少し異なったインタフェースを提供しているもう一つの HTML パーザは、 `HTMLParser` モジュールで使うことができます。

class `sgmllib.``SGMLParser`

  `SGMLParser` クラスは引数無しでインスタンス化されます。このパーザは以下の構成を認識するようにハードコードされています:

  - `<tag attr="value" ...>` と `</tag>` で表されるタグの開始部と終了部。
  - `&#name;` 形式をとる文字の数値参照。
  - `&name;` 形式をとるエンティティ参照。
  - `<!--text-->` 形式をとる SGML コメント。末尾の `>` とその直前にある `--` の間にはスペース、タブ、改行を入れることができます。

一つの例外が以下のように定義されます:

exception `sgmllib.``SGMLParseError`

  `SGMLParser` クラスで構文解析中にエラーに出逢うとこの例外が発生します。

  バージョン 2.1 で追加.

`SGMLParser` インスタンスは以下のメソッドを持っています:

`SGMLParser.``reset`()

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

`SGMLParser.``setnomoretags`()

  タグの処理を停止します。以降の入力をリテラル入力 (CDATA) として扱います。(この機能は HTML タグ `<PAINTEXT>` を実装できるようにするためだけに提供されています)

`SGMLParser.``setliteral`()

  リテラルモード (CDATA モード) に移行します。

`SGMLParser.``feed`(data)

  テキストをパーザに入力します。入力は完全なエレメントから成り立つ場合に限り処理されます; 不完全なデータは追加のデータが入力されるか、 `close()` が呼び出されるまでバッファに蓄積されます。

`SGMLParser.``close`()

  バッファに蓄積されている全てのデータについて、直後に EOF が来た時のようにして強制的に処理します。このメソッドは派生クラスで再定義して、入力の終了時に追加の処理を行うよう定義することができますが、このメソッドの再定義されたバージョンでは常に `close()` を呼び出さなければなりません。

`SGMLParser.``get_starttag_text`()

  最も最近開かれた開始タグのテキスト部分を返します。このテキストは必ずしも元データを構造化する上で必須ではありませんが、 "広く知られている (as deployed)" HTML を扱ったり、入力を最小限の変更で再生成 (属性間の空白をそのままにするなど) したりする場合に便利なことがあります。

`SGMLParser.``handle_starttag`(tag, method, attributes)

  このメソッドは `start_tag()` か `do_tag()` のどちらかのメソッドが定義されている開始タグを処理するために呼び出されます。 tag 引数はタグの名前で、小文字に変換されています。 method 引数は開始タグの意味解釈をサポートするために用いられるバインドされたメソッドです。 attributes 引数は `(name, value)` のペアからなるリストで、タグの `<>` 括弧内にある属性が収められています。

  name は小文字に変換されます。 value 内の二重引用符とバックスラッシュも変換され、と同時に知られている文字参照および知られているエンティティ参照でセミコロンで終端されているものも変換されます(通常、エンティティ参照は任意の非英数文字で終端されてよいのですが、これを許すと非常に一般的な `<A HREF="url?spam=1&eggs=2">` において `eggs` が正当なエンティティ参照であるようなケースを破綻させます)。

  例えば、タグ `<A HREF="http://www.cwi.nl/">` を処理する場合、このメソッドは `unknown_starttag('a', [('href', 'http://www.cwi.nl/')])` として呼び出されます。基底クラスの実装では、単に method を単一の引数 attributes と共に呼び出します。

  バージョン 2.5 で追加: 属性値中のエンティティおよび文字参照の扱い.

`SGMLParser.``handle_endtag`(tag, method)

  このメソッドは `end_tag()` メソッドの定義されている終了タグを処理するために呼び出されます。 tag 引数はタグの名前で、小文字に変換されており、 method 引数は終了タグの意味解釈をサポートするために使われるバインドされたメソッドです。 `end_tag()` メソッドが終了エレメントとして定義されていない場合、このハンドラは呼び出されません。基底クラスの実装では単に method を呼び出します。

`SGMLParser.``handle_data`(data)

  このメソッドは何らかのデータを処理するために呼び出されます。派生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行いません。

`SGMLParser.``handle_charref`(ref)

  このメソッドは `&#ref;` 形式の文字参照 (character reference) を処理するために呼び出されます。基底クラスの実装は、 `convert_charref()` を使って参照を文字列に変換します。もしそのメソッドが文字列を返せば `handle_data()` を呼び出します。そうでなければ、エラーを処理するために `unknown_charref(ref)` が呼び出されます。

  バージョン 2.5 で変更: ハードコードされた変換ではなく `convert_charref()` を使うようになりました。

`SGMLParser.``convert_charref`(ref)

  文字参照を文字列に変換するか、 `None` を返します。 ref は文字列として渡される参照です。基底クラスでは ref は 0--255 の範囲の十進数でなければなりません。そしてコードポイントをメソッド `convert_codepoint()` を使って変換します。もし ref が不正もしくは範囲外ならば、 `None` を返します。このメソッドはデフォルト実装の `handle_charref()` と属性値パーザから呼び出されます。

  バージョン 2.5 で追加.

`SGMLParser.``convert_codepoint`(codepoint)

  コードポイントを `str` の値に変換します。もしそれが適切ならばエンコーディングをここで扱うこともできますが、 `sgmllib` の残りの部分はこの問題に関知しません。

  バージョン 2.5 で追加.

`SGMLParser.``handle_entityref`(ref)

  このメソッドは ref を一般エンティティ参照として、 `&ref;` 形式のエンティティ参照を処理するために呼び出されます。 このメソッドは、 ref を `convert_entityref()` に渡して変換します。 変換結果が返ってきた場合、変換された文字を引数にして `handle_data()` を呼び出します; そうでない場合、 `unknown_entityref(ref)` を呼び出します。 標準では `entitydefs` は `&amp;` 、 `&apos;` 、 `&gt;` 、 `&lt;` 、および `&quot;` の変換を定義しています。

  バージョン 2.5 で変更: ハードコードされた変換ではなく `convert_entityref()` を使うようになりました。

`SGMLParser.``convert_entityref`(ref)

  名前付きエンティティ参照を `str` の値に変換するか、または `None` を返します。変換結果は再パーズしません。 ref はエンティティの名前部分だけです。デフォルトの実装ではインスタンス(またはクラス)変数の `entitydefs` というエンティティ名から対応する文字列へのマッピングから ref を探します。もし ref に対応する文字列が見つからなければメソッドは `None` を返します。このメソッドは `handle_entityref()` のデフォルト実装からおよび属性値パーザから呼び出されます。

  バージョン 2.5 で追加.

`SGMLParser.``handle_comment`(comment)

  このメソッドはコメントに遭遇した場合に呼び出されます。 comment 引数は文字列で、 `<!--` と``-->`` デリミタ間の、デリミタ自体を除いたテキストが収められています。例えば、コメント `<!--text-->` があると、このメソッドは引数 `'text'` で呼び出されます。基底クラスの実装では何も行いません。

`SGMLParser.``handle_decl`(data)

  パーザが SGML 宣言を読み出した際に呼び出されるメソッドです。実際には、 `DOCTYPE` は HTML だけに見られる宣言ですが、パーザは宣言間の相違 (や誤った宣言) を判別しません。 `DOCTYPE` の内部サブセット宣言はサポートされていません。 data パラメタは `<!`...`>` 記述内の宣言内容全体になります。基底クラスの実装では何も行いません。

`SGMLParser.``report_unbalanced`(tag)

  このメソッドは対応する開始エレメントのない終了タグが発見された時に呼び出されます。

`SGMLParser.``unknown_starttag`(tag, attributes)

  未知の開始タグを処理するために呼び出されるメソッドです。派生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行いません。

`SGMLParser.``unknown_endtag`(tag)

  未知の終了タグを処理するために呼び出されるメソッドです。派生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行いません。

`SGMLParser.``unknown_charref`(ref)

  このメソッドは解決不能な文字参照数値を処理するために呼び出されます。標準で何が処理可能かは `handle_charref()` を参照してください。派生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行いません。

`SGMLParser.``unknown_entityref`(ref)

  未知のエンティティ参照を処理するために呼び出されるメソッドです。派生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行いません。

上に挙げたメソッドを上書きしたり拡張したりするのとは別に、派生クラスでは以下の形式のメソッドを定義して、特定のタグを処理することもできます。入力ストリーム中のタグ名は大小文字の区別に依存しません; メソッド名中の tag は小文字でなければなりません:

`SGMLParser.``start_tag`(attributes)

  このメソッドは開始タグ tag を処理するために呼び出されます。 `do_tag()` よりも高い優先順位があります。 attributes 引数は上の `handle_starttag()` で記述されているのと同じ意味です。

`SGMLParser.``do_tag`(attributes)

  このメソッドは `start_tag()` メソッドが定義されていない開始タグ tag を処理するために呼び出されます。 attributes 引数は上の `handle_starttag()` で記述されているのと同じ意味です。

`SGMLParser.``end_tag`()

  このメソッドは終了タグ tag を処理するために呼び出されます。

パーザは開始されたエレメントのうち、終了タグがまだ見つかっていないもののスタックを維持しているので注意してください。 `start_tag()` で処理されたタグだけがスタックにプッシュされます。それらのタグに対する `end_tag()` メソッドの定義はオプションです。 `do_tag()` や `unknown_tag()` で処理されるタグについては、 `end_tag()` を定義してはいけません; 定義されていても使われることはありません。あるタグに対して `start_tag()` および `do_tag()` メソッドの両方が存在する場合、 `start_tag()` が優先されます。