Rでいらすとやからアナゴのイラストを取ってくる

追記(2016/03/17):@dichika氏から指摘を受けて、以下を修正しました。annotation_raster()に渡すのはrasterでなくてもいいらしいです(rasterでもいい)。

as.raster(httr::content(httr::GET(url))httr::content(httr::GET(url))


思い出はいつもキレイだけどそれだけじゃお腹がすくわ、というのはジュディマリの歌詞ですが、それだけじゃなくてもお腹がすきます。

例えば、そう、世間がAlphaGOで盛り上がっているときにアナゴのことを連想してしまったとき…

そんなとき、あなたならどうしますか?

やることは決まっていますね。そう、いらすとやからおいしそうなイラストをとってきて眺めることで心の空腹を満たします。もちろんRで。

該当ページのHTMLの取得

いらすとやには特にAPIなどは用意されていないため、Webスクレイピングでがんばります。いらすとやの検索結果画面のURLは、

http://www.irasutoya.com/search?q=クエリ

という形式になっています。まずは、このページをrvest::read_html()で取ってきます。

library(rvest)

get_result <- function(query) {
  read_html(sprintf("http://www.irasutoya.com/search?q=%s", query))
}

res <- get_result("アナゴ")

画像とそのキャプションを抜き出す

まずはいらすとやのHTMLをじっくり眺めます。すると、イメージの部分はこんな風になっていることがわかります(分かりやすいようにインデントをつけています)。

<div class='boxim'>
  <a href='http://www.irasutoya.com/2013/04/blog-post_705.html'>
    <script type='text/javascript'>
      document.write(bp_thumbnail_resize("http://2.bp.blogspot.com/-RB1mDuQvGkI/USyJ0W9QfKI/AAAAAAAAObc/Idip0N8CFUw/s72-c/nigirizushi_moriawase.png","握り寿司の盛り合わせ一人前のイラスト"));
    </script>
  </a>
</div>

ここで、肝心の画像とキャプションはJavascriptで動的に生成されていて、imgタグを指定するようなやり方ではうまく抜き取れません。まあさいわい今回はさほど複雑ではないので、stringr::str_extract()正規表現で抜き出すことにします。

(?<=)は肯定先読み、という正規表現です。このページが分かりやすかったです:正規表現の先読み・後読みを極める! - あらびき日記

詳しい解説は省きますが、以下のようなコードになります。

df <- res %>%
  html_nodes("div.boxim script") %>%
  html_text(trim = TRUE) %>%
  purrr::discard(~stringr::str_detect(., "default.png")) %>%
  purrr::map_df(
    ~list(url = stringr::str_extract(., "http://.*\\.png"),
          text = stringr::str_extract(., "(?<=png\\\",\\\").*(?=\"\\))")))

キャプションのHTMLエスケープを元に戻す

ここで、取れた中身を見てみると、変な文字列が紛れ込んでいることに気付きます。これはHTMLのエスケープです。

df$text
#> [1] "アナゴのお寿司のイラスト"             "握り寿司の盛り合わせ一人前のイラスト"
#> [3] "あなごのイラスト&#65288;魚&#65289;" 

convert HTML Character Entity Encoding in R - Stack Overflowによると、これを修正するのは一度XMLに直してからxml2::xml_text()で読む、というのがいいようです。

unescape_html <- function(str){
  purrr::map_chr(
    sprintf("<x>%s</x>", str),
    ~xml2::xml_text(xml2::read_html(.)))
}

df$text <- unescape_html(df$text)
df$text
#> [1] "アナゴのお寿司のイラスト"             "握り寿司の盛り合わせ一人前のイラスト"
#> [3] "あなごのイラスト(魚)"

ちゃんと意図通りの文字になっていますね。

プロットする

ggplot2で画像をプロットするときのやり方は以前にブログに書いたので割愛します。

notchained.hatenablog.com

library(ggplot2)

plot_void <- function(url, text) {
    # httr::content()はContent-Typeに応じて適切な関数を選んでレスポンスを変換してくれます
    # image/pngの場合はpng::readPNG()(詳しくは?httr::content参照)
    img <- httr::content(httr::GET(url))

    ggplot(data.frame(x=0,y=0),aes(x,y)) +
        annotation_raster(img, -Inf, Inf, -Inf, Inf) +
        coord_equal() +
        theme_void() +
        ggtitle(text)
}

x <- purrr::invoke_rows(plot_void, df)

# invoke_rowsの結果は.outに入っている
do.call(gridExtra::grid.arrange, x$.out)

f:id:yutannihilation:20160313010815p:plain

どうでしょうか?

...

見てるとお腹すきますね。。

結論

誰か?おごってください。