社会ノマド

備忘録と書きもの練習帳。とくに何らかのハンドリング系と、雑多な話題に関する読書録になるかなと思います。

ホテル街を見つける(1)Rでスクレイピング入門

以下の記事に至るまでの技術的なところをメモしておく。長丁場になるので(1)スクレイピング編、(2)ジオコーディング編、(3)プロット編とする。それぞれR、R、QGISを用いた方法を紹介する。

zawazawalong.hatenablog.com

流れ

  1. 検索サイトからスクレイピング(R)◀今回!
  2. ジオコーディング(R)
  3. 地図上にプロット(QGIS

ということで、今回は Rでスクレイピング について。 スクレイピングとはwebサイトから情報を引っこ抜いてくること です。ド初心者なので、ネット上の「入門スクレイピング」すらよくわからん。コード解読しつつRでスクレイピングする方法をメモ。

スクレイピング全体の流れ

Rを使ってスクレイピングする。パッケージはrvestパッケージを用いる。(参照:ろーど とぅ KONA)。

まずはURLからHTMLを取得する。今回はラブホテル検索サイトハッピー・ホテルさんのサイトを使わせていただく。

データを引っ張ってくる

library(rvest)
library(dplyr)

# URLをhtml関数に渡すとHTMLが読み込まれる
html <- html("http://happyhotel.jp/searchArea.act?jis_code=13102")

# 必要な箇所を引っ張る
area = html %>%
    html_nodes(xpath = '////div/div/h2') %>%
    html_text()

これでHTMLを取得できる。読み込んだHTMLから必要な箇所を抽出し area ベクトルへしまう(HTMLについてはAppendix①参照)。

area に html をしまい、 html_nodes() 関数に渡す。html_nodes()は、xpath = に渡した条件のタグの中身を引っ張ってくるようである(このあたりよくわからない…)。今回はサイトのHTMLをじっと眺めた結果、h2タグの中に住所が入っていたので、divの下のdivの下のh2タグを与えた(結果うまく行ったのだけれど冗長かも)。ここでのポイントは、 ほしい情報がどのタグで挟まれているか 。頑張って見つけよう!

すると各h2ごとに引っ張ったものがリストに入って返ってくるので、 html_text() 関数に渡す。すると、取り出してきた変数がchar型で area の中にベクトルとしてしまわれる(このあたりわからないひとはAppendix②参照)。

データのクリーニング

引っ張ってきたデータが area に入ったが、改行\nやらタブ\tやらが邪魔。あと頭と尻尾で毎度余計な情報がくっついている。これと電話番号を消して 住所のみをベクトルにしまいたい

##「検索方法」「ハッピー・ホテル」の行(頭とお尻)を削除
area2 <- subset(area, !grepl("検索方法", area))
area2 <- subset(area, !grepl("ハッピーホテル", area))

##住所のみを抽出
address<-unlist(strsplit(area2, "\n\t\t\t"))
address <- subset(address, !grepl("TEL", address))
address<-subset(address,address!="")

##書き出す
write.csv(address, "test.csv", quote=FALSE, row.names=FALSE)

これできれいなデータができました!ディレクトリにtest.csvという名前で書き出しておきます。

ただし、これだと「ある1ページ」(東京都中央区のホテル情報)だけです。様々な地域(例えば全国)を一気に引っ張ってきたい!この点も踏まえての完成品が以下です。

スクレイピング完成品

地域コード問題

地域にはそれぞれ標準コードが振られています。都道府県が2桁で(例えば東京都は13)、さらにもう一つ下に3桁でコードが付いて5桁である都道府県の地域を表します。例えば東京都中央区は13と102がくっついて13102になる[^1]。総務省から標準地域コード一覧が落とせます。

引っ張ってくるときのアドレスhtml <- html("http://happyhotel.jp/searchArea.act?jis_code=13102") の一部(13102)をこれに変えてfor文を回せば東京都中央区以外のアドレスをそっくり引っ張れる、という発想で以下。

library(dplyr)
library(rvest)

#総務省からダウンロードしたコード
rcode<-read.csv("region_code.csv",header=T)

#fooに読み込み先アドレスを入れる
foo=NA
for (i in 1:length(rcode$tiiki.code)){
  foo[i]<-paste("http://happyhotel.jp/searchArea.act?jis_code=",rcode$tiiki.code[i],sep="")
}

#html読み込み(ここで時間が掛かる):一応アクセス/1秒で制限
page <- NA
for (i in 1:length(foo)){
  page <- cbind(page,html(foo[i]))
  Sys.sleep(1)
}
page<-page[-1]

#スクレイピング関数、n行1列の行列を得る
scrape<-function(x){
hotel <- data.frame(
  area = x %>%
    html_nodes(xpath = '//div/div/h2') %>%
    html_text(),
  stringsAsFactors = FALSE
)
}

#スクレイピングして縦につなぐ
address<-NA
for (i in 1:length(page)){
  address<-rbind(address,scrape(page[[i]]))
}
##[[]]はunlist(page[i])でもいけるかも
address<-address[-1,]

#余分な行を除く
##「検索方法」「ハッピー・ホテル」のノイズ行を削除
address <- subset(address, !grepl("検索方法", address))
address <- subset(address, !grepl("ハッピーホテル", address))
##住所のみを抽出
text<-unlist(strsplit(address, "\n\t\t\t"))
text <- subset(text, !grepl("TEL", text))
text<-subset(text,text!="")

write.csv(address, "address.csv", quote=FALSE, row.names=FALSE)

今回はここまで。次回はこの住所一覧を下に緯度経度を取得するジオコーディングを、Google APIを用いて行います。

Appendix

①HTML

HTMLについては以下に簡単で簡単なところだけ書いたので参照。 zawazawalong.hatenablog.com

②パイプ処理

あるとは知っていながら使ってなかったパイプ処理。簡単な説明です。以下の例だとdata.frameで作ったデータフレームを、次のサブセット第一引数に引き渡している。

# 通常の処理
a <- data.frame(x=1,y=1:10)
subset(a,y >= 6) 

# パイプ処理を用いる
library(dplyr)
data.frame(x=1,y=1:10) %>%
  subset(y >= 6) 

参照RPubs - このパッケージがすごい2014: magrittr