by Hippo2000(2001/1/25)
HTML::Parserなのです。
なお内容等が間違っていたら修正します。ご連絡ください。
HTML::Parser - HTML パーサー・クラス
これは新しいXSベースのHTML::Parserです。これはHTML::Parser バージョン2.2xと完全な後方互換ははずですが、多くの新しい機能を持ち、非常に速くなっています。
use HTML::Parser ();
# パーサー・オブジェクトの作成
$p = HTML::Parser->new( api_version => 3,
start_h => [\&start, "tagname, attr"],
end_h => [\&end, "tagname"],
marked_sections => 1,
);
# Parse document text chunk by chunk $p->parse($chunk1); $p->parse($chunk2); #... $p->eof; # signal end of document
# ファイルから直接解析する
$p->parse_file("foo.html");
# もしくは
open(F, "foo.html") || die;
$p->parse_file(*F);
HTML::Parser バージョン2スタイル のサブクラスかとメソッド・コールバック:
{
package MyParser;
use base 'HTML::Parser';
sub start {
my($self, $tagname, $attr, $attrseq, $origtext) = @_;
#...
}
sub end {
my($self, $tagname, $origtext) = @_;
#...
}
sub text {
my($self, $origtext, $is_cdata) = @_;
#...
}
}
my $p = MyParser->new;
$p->parse_file("foo.html");
HTML::Parserクラスのオブジェクトはマークアップを理解し、それをHTMLドキュメントに入っている平文(別名データ内容)から切り離します。異なるマークアップとテキストが理解されると、対応するイベント・ハンドラが呼び出されます。
HTML::Parserは汎用的なSGMLパーサーではありません。実際に"存在する"HTMLを扱えるようにしてきました、そしてW3cからの多くのHTML仕様に厳密に従うのではなく、通常は一般的なWebブラウザがやるような方法に近い形で解析します。一致しない部分では、多くの場合、公式の動きをとれるようにするオプションがあります。
解析されるドキュメントは任意の形式で与えることができます。これによりネットーワークからドキュメントを受け取ったとき、その場で解析する解析することを可能にします。
イベント・ドリブンな解析があなたのアプリケーションに合わないと思うのでしたら、HTML::TokeParserを使いたいかもしれません。それはHTML::Parserのサブクラスで、より伝統的なプログラム構造を可能にします。
以下のメソッドが新しいHTML::Parserオブジェクトを作成するために利用することができます:
最上位のキーが"<イベント>_h"(例えば"text_h")であれば、そのイベントへのハンドラを割り当てます。そうでなければパーサー・オプションを初期化します。イベント・ハンドラに指定する値は、配列リファレンスでなければなりません。複数のハンドラを'handlers => [%handlers]' で割り当てることもできます。下記の例をご覧ください。
もし引数なしでnew()が呼ばれたら、それはHTML::Parserのバージョン2と互換性のあるコールバック・メソッドを使うパーサーを作成します。詳細は下記の"バージョン2との互換性"をご覧ください。
特殊なコンストラクタ・オプション 'api_version => 2'は、他のオプションやハンドラを設定しながら、バージョン2コールバックを初期化するために使われます。何もオプションを設定したくなく、そしてv2互換モードしたくなければ、'api_version => 3'オプションを使うことができます。
例:
$p = HTML::Parser->new(api_version => 3,
text_h => [ sub {...}, "dtext" ]);
これは一般的なエンティティがデコードされた元のテキストを受け取るtextイベント・ハンドラ・サブルーチンを持った新しいパーサーを作成します。
$p = HTML::Parser->new(api_version => 3,
start_h => [ 'my_start', "self,tokens" ]);
これは$pとトークン配列を受け取るstartイベント・ハンドラ・メソッドを持った新しいパーサーを作成します。
$p = HTML::Parser->new(api_version => 3,
handlers => { text => [\@array, "event,text"],
comment => [\@array, "event,text"],
});
これはtextとcommentイベントでイベント・タイプと元のテキストを@arrayに格納する新しいパーサーオブジェクトを作成します・
以下のメソッドでHTML::ParserオブジェクトにHTMLドキュメントを食わせることができます。
呼び出されたイベント・ハンドラが$p->eofにより解析を中断させたら、$p->parse()はFALSE値を返します。
$fileにファイル名が入っていて、ファイルがオープンできなければ、メソッドは未定義値を返し、$!にはなぜ失敗したのかが入ります。そうでなければ、戻り値はパーサー・オブジェクトへのリファレンスです。
$file引数にファイルハンドルが渡されると、ファイルは通常はEOFまで読み込まれますが、クローズされません。
呼び出されたイベント・ハンドラが$p->eofにより解析を中断させたら、$p->parse_file()はファイル全体を読み込んでいないかもしれません。
複数バイトの行末文字を持つシステムでは、parse_fileがバイナリー・モードでないファイルハンドルで呼ばれているとoffsetとlength argspecsのために渡された値は、小さすぎるかもしれません。
$p->eofをハンドラの内側から呼び出すと、解析をその時点で終了させ、$p->parseにFALSE値を返させます。これは$p->parse_file()による解析も終らせます。
戻り値はパーサー・オブジェクトへのリファレンスです。
ほとんどのパーサー・オプションはブール値の属性により制御されます。各ブール値の属性は対応するメソッドをTRUE引数で呼び出すことにより有効になり、引数値をFALSEで呼ぶことにより無効になります。引数なしであれば、属性値は変更されないままです。各メソッドからの戻り値は古い属性値です。
パーサー・オプションの取得 そして/または 設定に使えるメソッドは以下の通りです:
この属性を有効にすることにより公式の動きが有効にすることができます。
<IMG SRC=newprevlstGr.gif ALT=[PREV LIST] BORDER=0>
デフォルトでは、"LIST]" は明らかに意図しているようなALT値の一部ではなく、ブール値の属性として解析されます。これはNetscapeでもそうなります。
この属性を有効にすることにより公式の動きが有効にすることができます。もし有効になると、"LIST]"が正しい属性名ではないために上位のタグはテキストとして報告されるようになります。
空要素タグは開始タグのように見えますが、文字の並びの最後に"/>"がつきます。HTML::Parserにより理解されるとき、startイベントとともに擬似的なendイベントを発生します。擬似的なendイベントのためのtextは空で、トークン配列での唯一の要素が正しいタグ名を持っているとしてもtokenpos配列は未定義になります。
XML処理命令はHTMLの場合での単なる">"の代わりに"?"で終ります。
現在はマーク付のセクション要素に関連付けられたイベントはありません。
マークアップとテキストが理解されると、ハンドラが呼び出されます。以下のメソッドは異なるイベントのためにハンドラを設定するために使うことができます:
eventはtext, start, end, declaration, comment, process または default. のどれか1つです。
Subroutine はイベントを扱うために呼ばれるサブルーチンへのリファレンスです。
Method_name はイベントを扱うために呼ばれる$pのメソッドの名前です。
Accum はsub-arraysとしてイベント情報を保持するための配列です。
2番目の引数が""であれば、イベントは無視されます。もしundefであれば、そのイベントのためにはdefaultハンドラが呼ばれます。
Argspec はそのイベントのために報告される情報を記述する文字列です。要求された情報のうちいずれかでもそのイベントに当てはまらなければ、undefとして渡されます。もしargspecが省略されると、最後の更新から変更されないままになります。
$p->handleからの戻り値は古いコールバック・ルーチンまたは蓄積する配列へのリファレンスです。
ハンドラ・コールバック・ルーチン/メソッドからの戻り値は常に無視されます。ハンドラ・コールバックは$p->eofメソッドを呼ぶことにより解析を中止するように要求することができます。ハンドラ・コールバックは$p->parse()または$p->parse_file()を呼び出すことが許されていません。
例:
$p->handler(start => "start", 'self, attr, attrseq, text' );
これは'start'イベントのために呼び出されるオブジェクト$pの"start"メソッドを発生させます。コールバックの形式は$p->start(\%attr, \@attr_seq, $text).です。
$p->handler(start => \&start, 'attr, attrseq, text' );
これは'start'イベントのためにサブルーチンstart()を呼び出させます。コールバックの形式はstart(\%attr,
\@attr_seq, $text)です。
$p->handler(start => \@accum, '"S", attr, attrseq, text' );
これは'start'イベント情報を@accumに保管させます。配列要素は['S', \%attr, \@attr_seq, $text].になります。
$p->handler(start => "");
これは'start'イベントを無視させます。これはstartイベントのためのすべてのデフォルト・ハンドラの呼出しも抑止します。これは$p->handler(start => sub {})と同じですが、より効率的です。
$p->handler(start => undef);
これはstartイベントに何もハンドラを関連付けません。もしデフォルト・ハンドラがあると、それが呼び出されます。
Argspecはイベントにより報告される情報を記述するカンマ区切りのリストが入った文字列です。以下のargspec識別子名を使うことができます:
declaration イベントでは、配列には各単語、コメント、そして宣言タイプで始まる区切られた文字列です。
comment イベントでは、これは各サブ・コメントが入ります。$p->strict_commentsが有効でなければ、1つのサブコメントだけが入ります。
start イベントでは、これには属性名/値の組が後ろについた元のタグ名が入ります。ブール値属性の値は$p->boolean_attribute_valueによって設定された値または、$p->boolean_attribute_valueによって値が設定されていなければ属性名になります。
end イベントでは、これには元のタグ名が入ります。(1トークンのみ)
process イベントでは、こにれは処理命令が入ります。(1トークンのみ)
これはtextイベントではundefを渡します。
startイベントでのブール値属性は属性値のオフセットと長さは(0,0)になります。
これはもしイベントにトークンがなかったり(例えばtext)、空要素タグによりおこされる擬似的なend イベントのためにはundefを渡します。
これらのオフセットと長さをテキストを変更するために使うのであれば、右から左にやるかオフセットを変更するために非常に注意深く計算しなければなりません。
declaration イベントでは、これは宣言タイプになります。
start とend イベントでは、これはタグ名になります。
process とstrictでないcomment eventイベントでは、タグのなかのすべてになります。
もしイベントにトークンがなければundefを渡します。
XMLでは大文字小文字を区別するので、xml_modeが有効であるとタグ名の大文字小文字は変更されません。
ちょっとおかしいかもしれませんが、declaration 要素の宣言タイプもタグ名として渡されます。事実、現在の実装ではタグ名は名前が小文字に供せさせられるかもしれないということを除いてはtoken0 と同じです。
ブール属性の値は$p->boolean_attribute_valueによって設定された値または$p->boolean_attribute_valueによって何も値が設定されなければ属性です。
これはstartイベントを除いてはundefを渡します。
xml_modeが有効でなければ、属性名は小文字に強制されます。
属性値での一般的なエンティティはデコードされ、属性値の中に入っているマッチするクォートの1つのレイヤは削除されます。
これはstartイベントを除いてはundefを渡します。
xml_modeが有効でなければ、属性名は小文字に強制されます。
エンティティデコードのためにはISO8859-1文字セット(いわゆるLatin1)が想定されます。
どこかの時点でHTML::Parserはutf8オプションをとることを予定しています。これは127よりも大きいコードをもつ文字をデコードするときにバイト並びに影響を与えます。
text イベントを除いてはundefを渡します。
textイベントでこのフラグがFALSEであれば、通常はさらにテキストを処理する前にdtext を使うか、エンティティを独自にデコードすることができます。
イベント名はtext, start, end, declaration, comment, process または default. のどれかです。
Lineは渡されたイベントの開始の行番号を発生します。ドキュメントの最初の行は1です。行カウントはあるハンドラがこの値を要求するまでは開始しません。
以下のイベントのためのハンドラを登録することができます:
パーサーは2つのtextイベントの間の単語または空白の並びを分けないことを保証します。
例:
<A HREF="http://www.perl.com/";>
例:
</A>
典型的なHTMLドキュメントでは、見つけることがありそうな宣言は<!DOCTYPE ...>です。
例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html40/strict.dtd";>
<!DOCTYPE ...>内のDTDはHTML::Parserを混乱させます。
例:
<!-- This is a comment -- -- So is this -->
処理命令のフォーマットと内容はシステムとアプリケーションに依存します。
例:
<? HTML processing instructions > <? XML processing instructions ?>
引数なしでHTML::Parserオブジェクトが構築されると、古いHTML::Parserバージョン2コールバック・メソッドと互換性のある、いくつかのハンドラが自動的に提供されます。
これは以下のメソッド呼出しと同等です:
$p->handler(start => "start", "self, tagname, attr, attrseq, text");
$p->handler(end => "end", "self, tagname, text");
$p->handler(text => "text", "self, text, is_cdata");
$p->handler(process => "process", "self, token0, text");
$p->handler(comment =>
sub {
my($self, $tokens) = @_;
for (@$tokens) {$self->comment($_);}},
"self, tokens");
$p->handler(declaration =>
sub {
my $self = shift;
$self->declaration(substr($_[0], 2, -1));},
"self, text");
これらのハンドラの設定は"api_version => 2"コンストラクタ・オプションで要求することもできます。
HTML::Parserクラスはサブクラスを作成することができます。Parserオブジェクトは単なるハッシュで、HTML::Parserは"_hparser"から始まるハッシュキーを予約しているだけに過ぎません。パーサーの状態はnew()と同じ引数を取るinit()メソッドを呼び出すことにより設定することができます。
最初の簡単な例はどのうようにしてHTMLドキュメントからコメントをはずすことができるかを示します。commentハンドラで何もせず、defaultハンドラではそれ以外を出力するようにして実現します:
use HTML::Parser;
HTML::Parser->new(default_h => [sub { print shift }, 'text'],
comment_h => [""],
)->parse_file(shift || die) || die $!;
次の簡単な例はHTMLドキュメントの<title>要素の内側のテキストを出力します。ここではstartハンドラを設定することから始めます。title開始タグを見つけたら、それは見つかったすべてのテキストを出力するtextハンドラと見つかったtitle終了タグが見つかるとすぐに終らせるendハンドラを有効とします:
use HTML::Parser ();
sub start_handler
{
return if shift ne "title";
my $self = shift;
$self->handler(text => sub { print shift }, "dtext");
$self->handler(end => sub { shift->eof if shift eq "title"; },
"tagname,self");
}
my $p = HTML::Parser->new(api_version => 3); $p->handler( start => \&start_handler, "tagname,self"); $p->parse_file(shift || die) || die $!; print "\n";
さらなる例がHTML-Parserディストリビューションの"eg/"に入っています;プログラムhrefsubはドキュメントで見つかったすべてのリンクの編集の方法を示します。またhtextsubはテキストだけを編集する方法を示します;プログラムhstripはあるタグ/要素 そして/または属性をどのように取り除くかを示します;そしてプログラムhtextはscript/styleの内容を除いた、プレーン・テキストをどのように取得するかを示します。
HTML::Parserは</plaintext>を見つけても<plaintext>モードのままです。Plaintextモードは本当にエスケープ可能であるきるべきではありません。
<style>と<script>セクションは最初の"</"で終りません。しかし完全に対応する終了タグが必要です。
strict_comment が有効なとき、まだ偶数あるいは奇数の"--"マーカーの間の空白でないものもコメントとして理解してしまいます。
一度$p->boolean_attribute_value が設定されると、デフォルトの動きに戻す方法がありません。
今のところ、両方のクォート文字を同じリテラル argspecに入れる方法がありません。
空タグ、例えば"<>"と"</>"は理解されません。SGMLは前の開始タグあるいは対応する前の開始タグを閉じるために繰り返すことを許しています。
NETタグ、例えば"code/.../"は理解されません。これは"<code>...</code>"のためのSGML省略形です。
閉じられていない開始または終了タグ、例えば"<tt<b>...</b</tt>"は理解されません。
以下のメッセージがHTML::Parserにより作成されます。このリストでの書き方はperldiagでのものと同じです。
HTML::Entities, HTML::TokeParser, HTML::HeadParser, HTML::LinkExtor, HTML::Form
HTML::TreeBuilder ( HTML-Tree ディストリビューションの一部)
http://www.w3.org/TR/REC-html40
マークがついたセクションと処理命令についてのさらなる情報はhttp://www.sgml.u-net.com/book/sgml-8.htm.
にあるでしょう。
(原文のまま)
Copyright 1996-2000 Gisle Aas. All rights reserved. Copyright 1999-2000 Michael A. Chase. All rights reserved.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。