印刷用表示へ切り替え 通常表示へ切り替え 更新履歴を表示 更新履歴を隠す
Perl HTML::TreeBuilder で構築した木が元の HTML に戻らない

HTML::TreeBuilder でパースして木を作って元に戻すと,元通りにならない。p の終了タグが消えてしまう。調べてみると,これはバグではなく,設定が不十分なのが原因だった。

■ ■ ■

はじめに

HTML::TreeBuilder は,HTML 文書を構文解析して木を作ってくれる Perl モジュールである (HTML::TreeBuilder)。木の変更,追加,削除も可能で,処理後に HTML 形式で出力することができる,とても強力なツールである。

HTML::TreeBuilder を使って MT のプラグインを書いてみたところ,作った木が元に戻らないことが発覚。捨ててしまおうかと思ったが,マニュアルをよく読むと答が書いてあった。濡れ衣着せて悪いことしたな,TreeBuilder。

環境は,Perl 5.8.6, HTML::TreeBuilder 3.23, HTML::Element 3.23, HTML::Parser 3.56, HTML::Tagset 3.20 である。

構文解析して元に戻す

まず,HTML::Element の例にある HTML に p 要素を追加し,構文解析してから元に戻してみる。プログラムは次の通り。

#!/usr/bin/perl

use strict;
use HTML::TreeBuilder;

my $html = <<'END';
<html lang='en-US'>
  <head>
    <title>Stuff</title>
    <meta name='author' content='Jojo'>
  </head>
  <body>
    <h1>I like potatoes!</h1>
    <p>I like potatoes and tomatoes!</p>
  </body>
</html>
END

my $h = HTML::TreeBuilder->new;

$h->p_strict(1);
$h->parse($html);
$h->eof();
$html = $h->as_HTML("<>&", "  ");
$h->delete;

print $html;

HTML::Element ドキュメントの例題の HTML に「<p>I like potatoes and tomatoes!</p>」を追加してある。構文解析は HTML::Parser で行われる (HTML::Parser)。これを実行すると,次の結果となる。

<html lang="en-US">
  <head>
    <title>Stuff</title>
    <meta content="Jojo" name="author" />
  </head>
  <body>
    <h1>I like potatoes!</h1>
    <p>I like potatoes and tomatoes!</body>
</html>

終了タグ </p> が入らずに,</body> が来てしまっている。プログラムでは,閉じ忘れのタグを修正して追加するメソッド「$h->p_strict(1);」を呼んでいるのに,自らが閉じ忘れているではないか。ムカーッとして,捨てそうになったが,心を落ち着けてドキュメントを読んでみると,次の記述があった。

$h->as_HTML($entities, $indent_char, \%optional_end_tags)
If \%optional_end_tags is specified and defined, it should be a reference to a hash that holds a true value for every tag name whose end tag is optional. Defaults to \%HTML::Element::optionalEndTag, which is an alias to %HTML::Tagset::optionalEndTag, which, at time of writing, contains true values for p, li, dt, dd. A useful value to pass is an empty hashref, {}, which means that no end-tags are optional for this dump. Otherwise, possibly consider copying %HTML::Tagset::optionalEndTag to a hash of your own, adding or deleting values as you like, and passing a reference to that hash.

HTML::Element - Class for objects that represent HTML elements

as_HTML メソッドでは,「p, li, dt, dd の4つのタグについては,デフォルトでは終了タグを省略する」ということになっているのだ。そして,「これがイヤな人は \%optional_end_tags に {} を指定してね」となっているではないか。そこで,

$html = $h->as_HTML("<>&", "  ", {});

としたところ,見事終了タグが出力されるようになったのだった。

<html lang="en-US">
  <head>
    <title>Stuff</title>
    <meta content="Jojo" name="author" />
  </head>
  <body>
    <h1>I like potatoes!</h1>
    <p>I like potatoes and tomatoes!</p>
  </body>
</html>

メデタシメデタシ。なお,終了タグの省略を明示的に指定することもできる。病的ではあるが,次のように

$html = $h->as_HTML("<>&", "  ", {'h1' => 1});

とすると,h1 の終了タグが省略される。このように \%optional_end_tags を指定した場合,デフォルトの p, li, dt, dd の終了タグの指定はなくなる。

「\%optional_end_tags は,ハッシュセット %HTML::Tagset::optionalEndTag と同一のものである」とあるので,このコードを調べてみると,次のようになっていた。

%optionalEndTag = map {; $_ => 1 } qw(p li dt dd); # option th tr td);

map {} の中にある,最初のセミコロン「;」は何をしているのだろうか? 私の理解の範囲を超えている。

まとめ

HTML::TreeBuilder で HTML を出力するときは,as_HTML のオプションを最後まで指定すること。

$h->as_HTML("<>&", "  ", {});

strict な人は特にね。

Posted by n at 2008-03-12 21:58 | Edit | Comments (0) | Trackback(0)
Trackbacks

  • 「手違いで複数トラックバックを送ってしまった!」という場合でも気にしないでください (重複分はこちらで勝手に削除させていただきます)
  • タイムアウトエラーは,こちらのサーバの処理能力不足が原因です (詳細は トラックバック送信時のエラー をご覧ください)
  • トラックバックする記事には,この記事へのリンクを含めてください(詳細は 迷惑トラックバック対策 をご覧ください)
Comments
Post a comment
  • 電子メールアドレスは必須ですが,表示されません (気になる場合は「メールアドレスのような」文字列でもOKです)
  • URL を入力した場合はリンクが張られます
  • コメント欄内ではタグは使えません
  • コメント欄内に URL を記入した場合は自動的にリンクに変換されます
  • コメント欄内の改行はそのまま改行となります
  • 「Confirmation Code」に表示されている数字を入力してください (迷惑コメント対策です)


(必須, 表示されます)


(必須, 表示されません)


(任意, リンクされます)


Confirmation Code (必須)


Remember info (R)?