TreeBuilder を使って,日本語と文字参照を含む文書を処理する場合の手順について,はまったのでまとめておく。
HTML::TreeBuilder が文字参照を勝手にデコードしてしまうので困った。便利なのだが,扱いが難しいのだ (HTML::TreeBuilder)。文字参照というのは,「&」が頭についている文字のこと (文字参照 - Wikipedia)。日本語と文字参照を含む文書を処理する場合の手順についてまとめる。
Encodeモジュールを使って、HTMLテキストを事前にdecodeメソッドで内部表現(UTFフラグありのUTF-8)に変更、処理後にencodeメソッドで元の文字コードに戻すとよいようです。
日本語処理の基本はこれで。
$tree->parse( encode_entities($html, '&') ); とか、&を余分にエスケープしてからパースさせるようにして decode_entities( $tree->as_XML ); とかして取り出している。
そうなのよ。私も TreeBuilder のエスケープ関係は微妙だと思う。エスケープしたりしなかったりするし,テキスト部分の自動デコードを止めさせられない。属性の自動デコード抑制はできるのに。
decode_entitiesした時点でUnicodeになっているので、あとは utf-8 で出力したければ、encode('utf-8', decode_entities('格安')) したらよいようです。ちなみに euc-jp にしたけりゃ encode('eucjp', decode_entities('格安')); です。
Unicode と UTF-8 は違う模様。
まとめると次のようになる。日本語を文字化けしないようにして,文字参照を元の HTML のままにする手順。
エンコードしてデコードして処理,デコードしてエンコードして終了ということになっていて,ややこしい。encode_entities と encode の順序が構造的になっていないのが気になるが,TreeBuilder の直前で UTF フラグを立てたくて,一番最後に目的の文字コードに変更したいことを優先すると,こうなってしまう。
HTML 文書の文字コードを EUC-JP だとする (日本語は使ってないけど)。
動作を確認するため,最後の decode_entities は意識的にコメントアウトしてある (実際は入れること)。guts を使っているのは,インプリシットに挿入されてしまうタグを表示したくないから (nlog(n): HTML::TreeBuilder のメソッド名がグロい)。実行結果は次の通り。
この結果を見ると,あれれ? である。p の外側にある文字参照「©」のエスケープが足りない。このまま decode_entities すると,「©」が元に戻らなくなってしまう。ということは,as_HTML メソッドでエンコードしているということか。
とすれば「©」になる。最初のサンプルソースの一部を,これで置き換えればよい。encode_entities して encode_entities することになっているというのが変な感じではある。
最初の方にある「$html = encode_entities($html, '&');」の部分は次のように書き換えることもできる。
テキストノードと属性のエンコードを別々に処理するというもの。テキストノード (上のHTMLでは「>」の部分) のときだけに発生するイベントを捕まえて,encode_entities している。ハンドラの使い方については,HTML::Parser のドキュメントに記述がある (HTML::Parser)。属性については,テキストではないので,別に attr_encoded する必要がある。しかし,これだと,encode_entities と decode_entities の対がぱっと見分かりづらい。
最初,このハンドラ設定を見つけたときに,「文字参照の問題はこれで解決!」と思って,次のように書いてみた。
そうしたら,何も変わらなかった。テキストを引っ張り出してきて,元のところに入れるだけなので,何もやっていないのと等価だったのだ。
Posted by n at 2008-04-02 00:58 | Edit | Comments (0) | Trackback(0)
Master Archive Index
Total Entry Count: 1957