WebからHTMLを取得して保存しておく際に、スタイルの情報も含めて記録しておきたい時がある。CSSBoxを使ってstylesToDomとかすればうまく記録できそうだったのでやってみた。
具体的には下記のようなコードで、org.fit.cssbox.css.DOMAnalyzerのstylesToDom()、stylesToDomInherited()なんかを設定することでうまくいった。
import java.io.FileOutputStream import java.io.OutputStreamWriter import org.apache.xml.serialize.OutputFormat import org.apache.xml.serialize.XMLSerializer import org.fit.cssbox.css.CSSNorm import org.fit.cssbox.css.DOMAnalyzer import org.fit.cssbox.io.DefaultDOMSource import org.fit.cssbox.io.DefaultDocumentSource import org.fit.cssbox.layout.BrowserCanvas def download(url: java.net.URL) { // URLからDOMを取る val doc = managed(new DefaultDocumentSource(url)) { source => new DefaultDOMSource(source).parse() } // DOMAnalyzerでCSSを読むように設定 val analyzer = new DOMAnalyzer(doc, url) analyzer.attributesToStyles() // CSS周りの設定 analyzer.addStyleSheet(null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT) analyzer.addStyleSheet(null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT) analyzer.addStyleSheet(null, CSSNorm.formsStyleSheet(), DOMAnalyzer.Origin.AGENT) // 設定したCSSの読み込み(メソッド名はgetだけどvoid型) analyzer.getStyleSheets() // スタイルシートの内容をDOMのstyle属性に入れる設定 analyzer.stylesToDom() analyzer.stylesToDomInherited() // 編集したDOMを出力 managed(new FileOutputStream("result.html")) { os => val serializer = new XMLSerializer() val outputFormat = new OutputFormat() outputFormat.setIndenting(false); serializer.setOutputFormat(outputFormat) serializer.setOutputCharStream(new OutputStreamWriter(os)) serializer.serialize(doc) } }
これで試しにうちのサイトを読み込んでみると、analyzer.stylesToDom()を削った場合は、下記のような出力になる。(一部抜粋)
<div class="section"> <h2>メニュー</h2> <div class="menu_link"><a class="menu" href="/programming/">プログラミングメモ</a></div> <div class="menu_description">形態素解析やScala等、雑多な内容のメモ書き</div>
analyzer.stylesToDom()を入れた場合は、下記のようにスタイル等の情報がわんさか記述された状態になる。
<div class="section" style="background-color: #ffffff;border-bottom-color: #70a0a0;border-bottom-left-radius: 15.0px 15.0px;border-bottom-right-radius: 15.0px 15.0px;border-bottom-style: solid;border-bottom-width: 1.0px;border-left-color: #70a0a0;border-left-style: solid;border-left-width: 1.0px;border-right-color: #70a0a0;border-right-style: solid;border-right-width: 1.0px;border-top-color: #70a0a0;border-top-left-radius: 15.0px 15.0px;border-top-right-radius: 15.0px 15.0px;border-top-style: solid;border-top-width: 1.0px;color: #111111;display: block;font-family: sans-serif;line-height: 1.12;margin-bottom: 10.0px;margin-left: 5.0px;margin-right: 5.0px;margin-top: 10.0px;padding-bottom: 5.0px;padding-left: 15.0px;padding-right: 15.0px;padding-top: 15.0px;width: 100.0%;"> <h2 style="color: #004060;display: block;font-family: sans-serif;font-size: 120.0%;font-weight: bolder;line-height: 1.12;margin-bottom: 0.0px;margin-left: 0.0px;margin-right: 0.0px;margin-top: 0.0px;padding-bottom: 0.0px;padding-left: 0.0px;padding-right: 0.0px;padding-top: 0.0px;">メニュー</h2> <div class="menu_link" style="color: #111111;display: block;font-family: sans-serif;font-size: 100.0%;font-weight: bold;line-height: 1.12;margin-bottom: 5.0px;margin-left: 0.0px;margin-right: 0.0px;margin-top: 20.0px;"><a class="menu" href="/programming/" style="color: #406060;font-family: sans-serif;font-weight: bold;line-height: 1.12;text-decoration: none;">プログラミングメモ</a></div> <div class="menu_description" style="color: #606060;display: block;font-family: sans-serif;line-height: 1.12;margin-bottom: 10.0px;margin-left: 20.0px;margin-right: 20.0px;margin-top: 0.0px;">形態素解析やScala等、雑多な内容のメモ書き</div>
スタイルの情報が付いているので、そのままブラウザで開いてもあまり崩れずに表示してくれる。例えばYahooのニュースページなんかは下記のようにそこそこ原型を保って表示される。
gihyo.jpあたりもきっちり再現できた。
JavaScriptは読み込めないので、そっちでグリグリ書いてるページはダメで、Googleやはてなブックマークなんかはまったく表示できなかった。
あと、通信して、HTMLを取ってきて、パースして、CSSも取ってきて、DOMに適用して、といった処理をしているので実行時間はそれなりにかかる。重いサイトだと5秒以上かかることも。
個人的にはこうやって取得したHTMLを再度CSSBoxで読み込んで、width, height, absoluteXPos, absoluteYPosなんかを取り出す用途で使用した。それなりに現物に近い各情報が取れている。情報としてないよりあった方がだいぶ良いはず。