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なんかを取り出す用途で使用した。それなりに現物に近い各情報が取れている。情報としてないよりあった方がだいぶ良いはず。