先ほど使ってみたDispatchですが、core以外にもいくつかモジュールがあるようで、今回はその中でも興味のあったjsoupとの連携を試してみます。
dispatch-jsoup
https://github.com/dispatch/reboot/tree/master/jsoup
jsoup自体は、Javaで利用可能なHTML5向けパーサです。
jsoup: Java HTML Parser
http://jsoup.org/
このサイトでも、The Validator.nu HTML Parserと合わせて紹介させていただきました。
Javaで使える、HTML5パーサ
http://d.hatena.ne.jp/Kazuhira/20140107/1389108413
jQueryのようなCSSセレクタで要素を選択できたりするので、便利です。
で、Dispatchのjsoup向けのモジュールを利用するには、以下の様に定義します。
libraryDependencies ++= Seq( "net.databinder.dispatch" %% "dispatch-jsoup" % "0.11.0", "org.scalatest" %% "scalatest" % "2.0" % "test" )
ScalaTestは、テストコード用です。
そして、dispatch-jsoupを使うテストコードを用意します。
src/test/scala/org/littlewings/dispatch/jsoup/DispatchJsoupSpec.scala
package org.littlewings.dispatch.jsoup import scala.collection.JavaConverters._ import dispatch._ import dispatch.Defaults._ import org.scalatest.FunSpec import org.scalatest.Matchers._ class DispatchJsoupSpec extends FunSpec { describe("dispatch jsoup spec") { // ここに、テストコードを書く!! } }
まずは、jsoupのDocumentを取得してみましょう。対象は、Dispatchのオフィシャルサイトのトップページとします。ここからDocumentを取得して、コンテンツの中身を確認してみましょう。
it("get document") { val request = host("dispatch.databinder.net") / "Dispatch.html" val document = Http(request OK as.jsoup.Document).apply() document.text() should include ("Dispatch is a library for asynchronous HTTP interaction.") // タグを指定して取得 val h3Texts = document.getElementsByTag("h3").asScala.map(_.text().init) h3Texts should have size (4) h3Texts should contain theSameElementsInOrderAs Array("Diving in", "Defining requests", "Deferring action", "Demanding answers") }
as.jsoup.Document(正確には、dispatch.as.jsoup.Document)というResponseHandlerを使用することで、Dispatchを使用したHTTPリクエストの結果が、jsoupのDocumentとなります。
とはいえ、as.jsoup.Documentの中身は、いたって簡単ですが。
https://github.com/dispatch/reboot/blob/master/jsoup/src/main/scala/as/soup.scala
object Document extends (Response => nodes.Document) { def apply(r: Response) = Jsoup.parse(dispatch.as.String(r), r.getUri().toString) }
あとは、jsoupのAPIに沿ってプログラムを書いていけばOKです。
ところで、妙なところにinitが入っているのは、コンテンツ中にちょっと表示できなさそうな文字が入り込んでいるからです…。
val h3Texts = document.getElementsByTag("h3").asScala.map(_.text().init)
最後にもうひとつ、Queryというのもご紹介します。
Queryを使用すると、いきなりjsoupのセレクタを渡すことができます。
it("query#1") { val request = host("dispatch.databinder.net") / "Dispatch.html" // Queryでタグを指定して取得 val h3Tags = Http(request OK as.jsoup.Query("h3")).apply() val h3Texts = h3Tags.asScala.map(_.text().init) h3Texts should have size (4) h3Texts should contain theSameElementsInOrderAs Array("Diving in", "Defining requests", "Deferring action", "Demanding answers") } it("query#2") { val request = host("dispatch.databinder.net") / "Dispatch.html" // Queryでidを指定して取得 val idDispatch = Http(request OK as.jsoup.Query("#Dispatch")).apply() idDispatch.text().init should be ("Dispatch") }
このQueryもやはり、DispatchのResponseHandlerとして実装されています。
as.jsoup.Documentと同じく、定義はめちゃくちゃ簡単です。
https://github.com/dispatch/reboot/blob/master/jsoup/src/main/scala/as/soup.scala
object Query { import org.jsoup.select.Elements def apply(query: String): Response => Elements = Document(_).select(query) }
ちょっとしたショートカットというところですね。こういうの見てると、いろいろとResponseHandlerが実装できそうな気がしますね。Async Http Clientも知らなくちゃという気もしますが。
その他、json4s-jacksonなどもあるので、機会があれば試してみます。