GrailsでWebスクレイピングをするときはjsoupで簡単・便利!(もちろんGroovyでもJavaでも)
先日、名古屋で学生向けGrails/Groovyハンズオンをやらせて頂きました。
その中で、学生さんから「任意のサイトからHTMLを取得して、HTML中の任意の要素を取得するのにスマートな方法はありませんか?」といったような質問を受けました。いわゆるWebスクレイピングですね。その場では、TagSoup http://ccil.org/~cowan/XML/tagsoup/ を紹介しつつ実演しました。手元にTagSoupを使ったコードがすでにあって、その場で時間をかけずに見せやすかったからです。
が、jsoup http://jsoup.org/ の方が使い方とかjavascriptと同じように書けてかっこ良いですね。なので、この記事ではjsoupで書きます。
まず、(Grailsを前提として) BuildConfig.groovy
のdependenciesにjsoupを追加してあげます。Mavenリポジトリのjsoupの最新版からGradleの記法を引っ張ってくればいいですね。
dependencies {
runtime 'org.jsoup:jsoup:1.7.2'
}
あとは、特定のアクションにアクセスしたら、対象のHTMLを取得して必要な要素だけ返すように書いてあげます。ここではYahooニュースのトップトピックスを取得し、アンカー付きで表示しています。
import org.jsoup.* import org.jsoup.nodes.* import org.jsoup.select.* class ScrapingController { def index() { Document doc = Jsoup.connect("http://dailynews.yahoo.co.jp/fc/").get() Elements topics = doc.select('#topTopics li a') render topics.join("<br>") } }
GroovyのスクリプトでGrapeを利用してjsoupを使う場合にはこんな感じになりますね。
@Grapes( @Grab(group='org.jsoup', module='jsoup', version='1.7.2') ) import org.jsoup.* import org.jsoup.nodes.* import org.jsoup.select.* Document doc = Jsoup.connect("http://dailynews.yahoo.co.jp/fc/").get() Elements topics = doc.select('#topTopics li a') def str = "" topics.each{ str += it.text() + "\n" } str
MavenリポジトリにはGrapeの記法もあるので楽ちんです。importも忘れずに!こっちはアンカー無しのプレーンテキストです。
ひとつ気をつけて欲しいのは、静的HTMLの上にAjaxで動的にHTML断片を追加しているようなページでは、ブラウザで表示したときに見えているHTML要素がセレクタに引っかかってこない、ということがあることです。
いずれにせよ、jsoupでは select()
でCSSやjQueryのセレクタと同じ書き方でHTMLの要素を取得できるので、スクレイピングのためにAPIをあれこれ調べる必要がありません。すてきですね!