RでのWeb スクレイピング入門


はじめに

久保研介研究会のサブゼミの資料です!パネルデータ加工と同様に限定公開にしてます。(2020.08. 公開しました。)
雑なコードが多いですが、日々Updateしていきます。わかりにくい説明等あれば、どしどし連絡ください!作成にあたって、このサイトを参考にさせて頂きました!ありがとうございます。

今日のゴール

この講義が終わったあとに「なんか、Webサイトからデータとってこれた。やったぁ〜」と思っていただければ、満点です!

今日やること

Webからデータをとってくる方法(Web スクレイピング)の初歩を学びます。コピペが上手く使えない時に有効な手法になります。
今回はgogo.gsというガソリンスタンドの情報がまとめられているWebサイトからスタンドの店舗情報(今回は店舗名と住所)を例にとって解説していきます!出店戦略を分析する際には、住所データは欠かせないですよね。

Webスクレイピングの手順

  1. 欲しいデータが載っているサイトを探す
  2. データがWebサイトのどの部分に記載されているかを把握する
  3. Rでコードを書いてデータを取得する

この3つのステップで出来ます。簡単ですが、Webサイトを作成用のプログラミング言語であるHTML・CSSあたりの知識が少し必要になります。とりあえずやってみることが大事なので、やりながら解説していきます。難しい話は極力避けます。

実際にやってみる!

1. 欲しいデータが載っているサイトを探す

今回の例では沖縄県那覇市のガソリンスタンド情報を取得してみます!
なのでサイトは、https://gogo.gs/search/?kw=&ac=47201 です。那覇市内に48店舗あるみたいですね。

2. データがWebサイトのどの部分に記載されているかを把握する

ここで先ほど紹介したHTML・CSSが登場します。まず、サイト上で右クリックしてみてください。検証という項目が出てくるはずなので、クリックしてください!

こんな画面になるかと思います。右に表示されているものが、HTML・CSSです。なんかかっこいいですよね。詳しい説明は避けますが、覚えてもらいたいこととしては、Webサイトは階層構造で書かれているということです。なので、欲しいデータがどの層にあるかが大事です!

では、今回欲しいデータを整理しておきましょう。上の画面でいうと、店舗名である若狭SS / 有村商事沖縄(株)とその住所である沖縄県那覇市若狭2-19-1の2つです。この2つが、Webサイト上でどの場所(層)にあるかだけを把握できればスクレイピング出来ちゃいます!!!

HTML・CSSが表示されている部分の左上にポインタ(矢印)のようなものがあると思います。そこをクリックして、欲しいデータにカーソルを合わせると、下のような画面になるはずです。

上の画面の右側(HTML・CSSが表示されている部分)をみてみると、ハイライトされている部分があります。その1つ上をみてみると、<h5 class="shop-name font-weight-bold">と書いてあります。ここが、店舗名が書かれているを示しています。classが含まれている部分を探すのがコツです。他の店舗名で試してみても同じであるはずです。厳密にいうと、h5は5番目の見出しのことで、これにshop-name font-weight-boldというclass(名前)がついてます。

住所に対して同じことをやってみると、<p class="shop-address">沖縄県那覇市若狭2-19-1</p>が該当します。pはパラグラフのことです。

これまでの話をまとめると、
Webサイト:https://gogo.gs/search/?kw=&ac=47201
Webサイト上での店舗名の場所:<h5 class="shop-name font-weight-bold">
Webサイト上での住所の場所:<p class="shop-address">沖縄県那覇市若狭2-19-1</p>
場所を見つける際には階層構造を意識しながら、classを含むコードを探すのがコツです。

3. Rでコードを書いてデータを取得する

準備

RでWebスクレイピングをする際には、rvestというライブラリを使用します。tidyverseも後で使用します。

install.packages("rvest")
library("rvest")
library(tidyverse)

URLを指定する

read_html()というコマンドで今回スクレイピングするWebページを指定しておきます。

html <- read_html("https://gogo.gs/search/?kw=&ac=47201")

Webサイト上の場所を指定してデータを取得

始めに店舗名から取得していきましょう。html_nodes()というコマンドでHTMLごと取得してから、html_text()で、文字列だけを取得しています。つまり、店舗名だけが抽出されます。

html_nodes()中のxpath=で欲しいデータの場所を指定しています。xpath=に関してhは、この記事を参考にしてみてください。Webサイト上での店舗名の場所は<h5 class="shop-name font-weight-bold">であったので、xpath = "//h5[@class = 'shop-name font-weight-bold']"と指定します。イメージとしては、"// データがある階層 [@class = 'class名']"こんな感じです。

gas_name_1 <- html_nodes(html, xpath = "//h5[@class = 'shop-name font-weight-bold']") %>% 
  html_text()

gas_name_1には、店舗名が入っていることが確認出来ます。<h5 class="shop-name font-weight-bold">という場所にあるテキストデータが取得されているはずです。

見やすくしたいので、取得したデータをデータフレームに変換します。data.frameで変換することが出来ます。()の中は列名 = データというイメージ。

store_1 <- data.frame(store = gas_name_1)

住所についても同じことをしていきます。Webサイト上での住所の場所は<p class="shop-address">沖縄県那覇市若狭2-19-1</p>でした。xpath=での指定が下のようになることを確認してください。

gas_address_1 <- html_nodes(html, xpath = "//p[@class = 'shop-address']") %>% 
  html_text()

address_1 <- data.frame(address = gas_address_1)

データを整える

今回は結合のkeyとなる変数はなく、ただ単に横方向で結合したいのでcbindというコマンドを使います。厳密には行番号で結合しています。

gas_1 <- cbind(store_1, address_1)

他のページのデータも取得する

今回のケースだとWebのページ数が5ページあります。先ほどまでの作業をあと4回数繰り返して、最後に結合すれば可能ですが、めんどくさいですね。。そこで、このような繰り返し作業を自動化してくれるfor構文を使って、コードを回してみます。gas_1というデータに48店舗分のデータが格納されているはずです。

for (i in 2:5){
  # ページごとのURLを作成
  URL <- paste("https://gogo.gs/search/?kw=&ac=47201&page=", i, sep = "")
  # そのURLを読み込む
  html <- read_html(URL)
  # 店名の取得
  gas_name <-  html_nodes(html, xpath = "//h5[@class = 'shop-name font-weight-bold']") %>% 
    html_text()
  store <- data.frame(store = gas_name)
  # 同様に住所
  gas_address <- html_nodes(html, xpath = "//p[@class = 'shop-address']") %>% 
    html_text()
  address <- data.frame(address = gas_address)
  # 店名と住所を横方向に結合
  gas <- cbind(store, address)
  # 前ページまでのデータと縦方向に結合
  gas_1 <- rbind(gas_1, gas)
}

おまけ①

"/"の後ろが会社名になっているので、抽出してcompanyという変数を新しく作成してみます。
とりあえず、店舗名を"/"で2つに分割してみます。str_split_fixedというコマンドを使用すると可能です。文字列の処理についてはstringrというライブラリが有効です。最初にインポートしたtidyverseというライブラリの中に入っています。

gas_store2 <- str_split_fixed(gas_1$store, pattern = "/", n = 2)

gas_store2というデータをみてみると、見事に2つに分割されていることがわかります。もとデータと横方向にくっつけて、gas_newというデータ名にしてみましょう。

gas_new <- cbind(gas_1, gas_store2)

データを整えていきます。store, address, 2という3つの変数のみ必要なので、selectコマンドで抽出します。2という変数名をcompanyという名前に変更します。文字通りrenameというコマンドで出来ます。

gas_new <- gas_new %>% 
  select(store, address, "2") %>% 
  rename(company = "2")

会社名の前に半角の空白が入っていますね。文字列の置換を行うsubというコマンドで消しておきましょう!

gas_new$company <- sub( " ", "", gas_new$company)

試しに会社名で店舗数を集計してみましょう。いつも通りのgroup_bysummariseです。

gas_sum <- gas_new %>% 
  group_by(company) %>% 
  summarise(number = n())

おまけ②

取得したデータをcsvファイルに出力してみましょう。write.csvとすることで、文字通り出力することが出来ます。()の中で、出力するcsvファイルの名前と、エンコーディング、行番号に関して指定しています。特に、エンコーディングを指定してあげないと、文字化けしてしまいます。

write.csv(gas_new, "gas_naha.csv", fileEncoding = "CP932", row.names = FALSE)

最後に

予想よりも簡単に出来ますよね!もっと難しいことをやろうとなると、HTML・CSSの知識を深めることが必要になります。といっても必要になった時に勉強すれば大丈夫。特別必要ない時に勉強してもやる気でないですよね、、個人的にスクレイピングしてみたけど、エラーが出る!などあれば連絡ください。一緒に戦いましょう。

地理情報講座リスト