HTML::TreeBuilder で HTML 文書の断片を構文解析して元に戻すと,余計な html タグなどをつけてくれる。回避する方法はないか調べてみると,一風変わったな名前のメソッドが見つかった。
HTML::TreeBuilder の話の続き。先の記事は,HTML::TreeBuilder の出力が strict になる方法についてだった (nlog(n): HTML::TreeBuilder で構築した木が元の HTML に戻らない)。もちろん,これはオリジナルのドキュメントにちゃんと書いてある。そんなものを「再発見」してどうするのだという気もするが,嵌{はま}りやすいことでもあるので,強調しておくことに多少の意味はあるだろうということで,深く考えないことにする。さて,今回は,HTML 文書の一部 (断片) を構文解析して元に戻すと,「戻りすぎる」という話である。これもドキュメントにちゃんと書いてある記述ではあるのだが (HTML::TreeBuilder)。
HTML::TreeBuilder は,デフォルトでは HTML 文書全体を解析の対象としている。<html> タグで始まり,</html> で終わる文書である (加えて,<?xml?> や <!DOCTYPE> にも対応している)。したがって,逆に,HTML 文書の一部であると,困ることがある。例えば,次のようなものである。
これを構文解析して元に戻すと,次の出力になる。
<li> に不足している </li> や ul タグなどを追加してくれるのは嬉しいのだが,余分なタグを追加してくれてしまうのである。html, head, body は元々書いてないので困るのだ。このテストに使ったプログラムは次の通り (前回と同じ)。
当然,この問題は著者も認識していて,その回避の方法も,ドキュメントに書かれている (HTML::TreeBuilder)。「HTML 文書の一部が対象の場合は,guts() メソッドを使え」。guts() は,余計な要素を付け加えないからである。動作をまとめると次のようになる。
@nodes = $root->guts()
$parent_for_nodes = $root->guts()
- リストコンテキストの場合,(暗黙でない) ノードのリストを返す
- スカラーコンテキストの場合,ノードの数により返す値が異なる
- ノードが0個の場合,undef を返す
- ノードが1個の場合,そのノードを返す
- ノードが2個以上の場合,親のノードを返す (親のノードを div 要素と仮定する)
このことを考えると,プログラムとしてはリストコンテキストで受け取るのがいいということになる。ノードの数によって返す値が変わらないからである。逆に言うと,スカラーコンテキストだと「div」がついてしまうのが痛いのだ。
したがって,html, head, body などの暗黙の要素を付け加えたくない場合は,木を HTML に変換する次の部分を,
以下のように書けばいいということになる。
guts() が返すリストの要素 $c がリファレンスのときは HTML 要素なので,as_HTML メソッドで変換する。$c がリファレンスでないときは,単なるテキストなので,変換しない。
「guts」は「内臓」の意味である。ギターやバイオリン族の「ガット弦」やテニスラケットの「ガット」もそうだ (実際は腸)。TreeBuilderの guts は,外の「皮」を付けないで,内臓を取り出すという意味だろうか。これを他の言葉で「get_contents」などと表現しても,ピンとこない。「content_list」メソッドは実装されているので,混乱の元にもなる。なかなかグロい (grotesque な) 表現であるが,的確であると言える。
TreeBuilder には,他にも「disembowel」というメソッドがある。これは「腸を取出す」という意味である (dis・em・bow・el - goo 辞書)。disembowel() メソッドは,guts() と同様な値を返すが,中身を取り除いてしまうという点が異なる。こちらは,グロを超えて,むしろホラーであると言えるのではなかろうか。好きだけど。
2008年4月14日追記:
文字参照が含まれている場合を考えると,次のように書いた方がいいかも知れません (nlog(n): HTML::TreeBuilder で日本語と文字参照を扱う場合の手順)。
Master Archive Index
Total Entry Count: 1957