top / index / prev / next / target / source

2008-12-17 diary: [Java] TagSoup を使った HTML正常化のサンプル

いがぴょんの日記 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

[Java] TagSoup を使った HTML正常化のサンプル

HTML正常化を実現する OSSツールのひとつが TagSoup です。この TagSoup を使った、単純な HTML正常化サンプルを作成しました。

(xhtmlではない) HTML 4.01 は、仕様として終了タグがオプションとなっているものがあります。さらに、そもそも文法的に間違った HTMLが世の中に多く存在しています。(だけれども HTMLブラウザが気合いで表示しています)このような背景もあり、世の中の HTML には、結構いいかげんなものがあります。これを プログラムで読み込もうとすると、いろいろ難しいのですが、これを解決するたに、HTML正常化をおこなうツールを利用したくなることがあります。

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import org.ccil.cowan.tagsoup.HTMLSchema;
import org.ccil.cowan.tagsoup.Parser;
import org.ccil.cowan.tagsoup.XMLWriter;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * TagSoup を利用して HTML を正常化するためのシンプルなサンプル。
 * TagSoup 1.1 で動作確認。
 */
public class TagSoupSimpleSample {
    public static void main(final String[] args) throws IOException,
            SAXException {
        final XMLReader parser = new Parser();

        final HTMLSchema schema = new HTMLSchema();
        parser.setProperty(Parser.schemaProperty, schema);

        final StringWriter output = new StringWriter();

        final XMLWriter serializer = new XMLWriter(output);
        parser.setContentHandler(serializer);

        // TODO 確認した時点では、これが期待通りには機能せず。
        // parser.setDTDHandler(serializer);
        // 仕方が無いので、以下の記述により DOCTYPE を強制的に出力させる。(html用)
        serializer.setOutputProperty(XMLWriter.DOCTYPE_PUBLIC,
                "-//W3C//DTD HTML 4.01 Transitional//EN");

        // <html> に名前空間をあらわす属性が付かないようにする。
        parser.setFeature(Parser.namespacesFeature, false);

        final InputSource input = new InputSource();
        input
                .setCharacterStream(new StringReader(
                        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
                                + "<HTML><head>\n"
                                + "<title>this is title\n"
                                + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Windows-31J\">\n"
                                + "<body>\n"
                                + "<p>aaa<br>bbb<p>あいうAAA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BBB      CCC<uso>\n"
                                + "</html>"));

        // 出力を (xhtmlではなく) html にセットします。
        serializer.setOutputProperty(XMLWriter.METHOD, "html");

        // XML宣言の出力を抑制します。
        serializer.setOutputProperty(XMLWriter.OMIT_XML_DECLARATION, "yes");

        // 属性へのデフォルト付与を抑制させます。
        parser.setFeature(Parser.defaultAttributesFeature, false);

        // 出力先の文字エンコーディングを指定します。
        serializer.setOutputProperty(XMLWriter.ENCODING, "Windows-31J");

        // 知らない名前の要素について寛大に処理します。(jsp対策において必要と想定)
        parser.setFeature(
                "http://www.ccil.org/~cowan/tagsoup/features/ignore-bogons",
                false);

        // パースを実施。
        parser.parse(input);

        System.out.println("HTML正常化結果: [" + output.toString() + "]");
    }
}

なお、このプログラムを動作させてみるとわかるのですが、いわゆる ASCII ではない文字について 文字参照で表現された出力がおこなわれます。(UTF-8 を指定すると素直に文字で出ます) この文字参照をデコードするなどは、上記のサンプルには含まれていません。それらを解決する実装は blancoHtmlNormalizer で実現しようと考えています。

関連する日記

登場キーワード


この日記について