ParserCallbackでHTMLのタグを解析する
続いて、HTMLのソースコードを解析して特定の要素を取り出す方法について考えてみましょう。誰もが考えそうなのは、正規表現を使ってタグの特定の要素を抜き出すというものですね。これでも確かに可能ですが、JavaにはHTMLソースコードを解析するのに便利なものが用意されていますからそれを使ってみましょう。
Javaには、HTMLなどの構文解析を行うクラスが用意されています。これらは一般に「パーサ」と呼ばれています。HTMLの解析には、javax.swing.text.html.HTMLEditorKit.Parserというクラスが用意されており、このクラスによりタグやデータを検出し、それらを処理するような仕組みになっています。ただし、このParserクラスはabstractクラスですので、通常はこれを継承したParserDelegatorクラスを用います。
Parserクラスでは、タグの種類ごとにメソッドを呼び出して処理を行います。このタグの処理を行うためのメソッド類は「パーサ・コールバック」と呼ばれるクラスとして作成し組み込むことができます。HTMLの場合、javax.swing.text.html.HTMLEditorKit.ParserCallbackというクラスが用意されています。ParserクラスでHTMLのパース処理を行う際、このParserCallbackクラスをコールバック用のクラスとして設定することで、タグごとの処理部分をParserCallbackが行うようにできるのです。
ということは、あらかじめParserCallbackクラスを定義しておき、これを使ってParserのパース処理を呼び出せば、独自の解析処理が行えるようになるはずです。
public class MyParserCallback extends ParserCallback {
private FreeHtmlAnalyzer an;
public MyParserCallback(FreeHtmlAnalyzer a){
super();
an = a;
}
public void handleSimpleTag(Tag tag, MutableAttributeSet attr, int pos) {
if (tag.equals(HTML.Tag.IMG)) {
String src = (String)attr.getAttribute(HTML.Attribute.SRC);
an.addSrc(src);
}
super.handleSimpleTag(tag, attr, pos);
}
public void handleStartTag(Tag tag, MutableAttributeSet attr, int pos) {
if (tag.equals(HTML.Tag.A)) {
String src = (String)attr.getAttribute(HTML.Attribute.HREF);
an.addHref(src);
}
super.handleStartTag(tag, attr, pos);
}
}
<img>タグと<a>タグの処理をするため、ParserCallbackを継承したMyParserCallbackクラスを定義してみました。ここでは、handleSimpleTagとhandleStartTagという2つのメソッドをオーバーライドしてあります。handleSimpleTagは、閉じタグを持たない1つだけのタグを処理するためのもので、<img>タグはこのメソッドにより処理されます。またhandleStartTagは閉じタグを持ったタグの解した具部分を処理するもので、<a>タグはこれにより処理されます。
こららのメソッドは、いずれも引数で渡されるTagインスタンスの種類を調べ、それに応じて処理を行わせるようになっています。たとえば、<img>タグかどうかをチェックするならば、引数のTagインスタンスがHTML.Tag.IMGかどうかを確認すればよいのです。<a>タグならば、HTML.Tag.Aかどうかをチェックします。
タグの中のsrcやhrefといった属性は、引数で渡されるMutableAttributeSetインスタンスのgetAttributeを使って取得できます。getAttributeでは、引数に取り出したい属性を指定します。これはHTML.Attribute内にクラスフィールドとして要されており、srcならばSRC、hrefならばHREFフィールドを指定します。
パーサのコールバック処理は、以下のような形で呼び出すことができます。
URL u = new URL(url.getText());
InputStream is = u.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
ParserDelegator pd = new ParserDelegator();
HTMLEditorKit.ParserCallback cb = new MyParserCallback(this);
pd.parse(br, cb, true);
読み込み先を示すURLからInputStream、更にはBufferedReaderを作成します。そしてParserDelegatorインスタンス、ParserCallbackインスタンスをそれぞれ作成し、ParserDelegatorのparseメソッドを呼び出します。これでInputStreamからデータを読み込み、これを解析してParserCallbackのメソッドを呼び出しながらHTMLの処理を行わせることができます。
今回は、<img>タグと<a>タグの属性を取りだすだけでしたが、ParserCallback継承クラスをカスタマイズすれば、更に他の要素を処理させることも可能になります。それぞれでカスタマイズに挑戦してみるのも面白いでしょう。