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を落としてきてレンダリングしているわけなので、対象ページの重さやネットワークの状態やらでかかる時間は変ってくる。