sudo -u grails

Grailsの記事が充実する。といいなぁ

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()CSSjQueryセレクタと同じ書き方でHTMLの要素を取得できるので、スクレイピングのためにAPIをあれこれ調べる必要がありません。すてきですね!