2012年09月13日

CSSBoxでHTMLのwidthとheightを取ってみる

HTMLの本文抽出をするライブラリを探していたら、ふとHTMLのwidthやheightを取りたい衝動に駆られたので、CSSBoxというJavaのレンダリングエンジンを使ってさらっと取得してみた。


下記ページを解析してみる。ヘッダ、フッタ、レフトナビ、コンテンツなどの要素がいる普通のブログページ。

http://blog.mwsoft.jp/article/57078793.html

下記のようなコードを実行。これで全要素(インライン要素も含む)のwidthとheightが標準出力される。

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.fit.cssbox.css.CSSNorm;
import org.fit.cssbox.css.DOMAnalyzer;
import org.fit.cssbox.demo.DOMSource;
import org.fit.cssbox.layout.Box;
import org.fit.cssbox.layout.BrowserCanvas;
import org.fit.cssbox.layout.ElementBox;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class CssBoxSample {

    public static void main(String[] args) throws IOException, SAXException {
        URL url = new URL("http://blog.mwsoft.jp/article/57078793.html");
        URLConnection con = url.openConnection();
        InputStream is = con.getInputStream();

        DOMSource parser = new DOMSource(is);
        Document doc = parser.parse();

        DOMAnalyzer analyzer = new DOMAnalyzer(doc, url);
        analyzer.attributesToStyles();
        analyzer.addStyleSheet(null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT);
        analyzer.addStyleSheet(null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT);
        analyzer.getStyleSheets();

        BrowserCanvas browser = new BrowserCanvas(analyzer.getRoot(),
                analyzer, new java.awt.Dimension(1000, 600), url);

        setWidth(browser.getRootBox().getSubBoxList());
    }

    private static void setWidth(List<Box> boxList) {
        for (Box box : boxList) {
            if (box instanceof ElementBox) {
                ElementBox ebox = (ElementBox) box;
                System.out.println(ebox + " : " + ebox.getWidth());
                setWidth(ebox.getSubBoxList());
            }
        }
    }
}

結果(一部抜粋、コメント部分は手書き)

// ヘッダ
<div id="header" class=""> : 1000 80
// レフトナビとコンテンツを囲ってるdiv
<div id="content" class=""> : 990 616
// レフトナビのa要素
<a id="" class="menu"> : 56 17
// コンテンツ部分
<div id="" class="blog"> : 772 587
// pre要素
<pre id="" class="prettyprint lang-bash"> : 730 139
// フッタ
<div id="footer" class=""> : 1000 45

BrowserCanvasに横幅1000を指定しているので、ヘッダやフッタ(widthに100%を指定している)は幅1000pxになっている。

幅や高さだけでなく、要素のX座標やY座標、マージン、背景色や枠線の色も取れる。

試しにpre要素(背景灰色)に対してgetBgcolorしてみると、java.awt.Color[r=243,g=243,b=243]が取れた。この値はHTML要素内ではなくCSSに指定しているもの。

入力されたURLからちゃんとCSSを取りに行って、パースして、背景色を取ってくれているらしい。パケットキャプチャで見たところ、画像もちゃんと取っていた。

試しに画像があるページに対して実行してみたところ、width指定のないimg要素は、ちゃんと画像サイズと同じwidthとheightになっていた。

JavaScriptで描画したコンテンツまではさすがに取ってくれなかった。


処理は多少重い。1ページ解析するのに1秒〜7秒程度。

画像やCSSを落としてきてレンダリングしているわけなので、対象ページの重さやネットワークの状態やらでかかる時間は変ってくる。