盆暗の学習記録

データサイエンス ,エンジニアリング,ビジネスについて日々学んだことの備忘録としていく予定です。初心者であり独学なので内容には誤りが含まれる可能性が大いにあります。

[R]Webサイトをスクレイピングしてxlsxをダウンロードする

  1. Rでスクレイピングするときの基本的な手順
  2. Rでネット上のデータをダウンロードするときの方法

についてメモ。

やりたいこと

例えば,以下のサイトで

http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html

f:id:nigimitama:20181218160957p:plain

  1. 「北海道」から「沖縄県」の各リンクをたどって,
  2. 各リンク先のすべてのxlsファイルのリンクを取得し,
  3. すべてのxlsファイルをダウンロードしたい

という状況を考えます。

①{rvest}によるスクレイピング

1.「北海道」から「沖縄県」の各リンクをたどって,
2.各リンク先のすべてのxlsファイルのリンクを取得

するところまでをスクレイピングで行います

# ライブラリ読み込み
library(tidyverse)
library(readr)
library(rvest)

1. htmlの読み込み

read_html()に目的のサイトのURLを入れ,htmlを取得します

# read_html():htmlの読み込み
html_data = read_html("http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html")

# 中身の表示
html_data
> # 中身の表示
> html_data
{xml_document}
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
[1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n<meta http-equiv="Content-Style-Type" content="text/ ...
[2] <body>\r\n<a name="pTop" id="pTop"></a>\r\n<noscript><div id="nsBelt"><div class="nsMsg">すべての機能をご利用いただくにはJavascriptを有効にして下さい。</di ...

2. 目的のタグ・URLを抽出

html_nodes()cssxpathを指定して目的のタグを抽出できます

div要素を抽出

今回の目的のサイトの,各都道府県のページへのリンクがある領域はbodyAreaというidのdivタグの中にあるので,まずbodyAreaの中の情報だけを取り出してみます。

Google Chromeの「検証(Ctrl+Shift+I)」から目的のタグで右クリックしてメニューを開き,「Copy XPath」をクリックすればxpathが入手できるので,Rに貼り付けます

f:id:nigimitama:20181218162436p:plain

# html_nodes():cssやxpathを指定して目的のタグを抽出
bodyArea = html_data %>% html_nodes(xpath = '//*[@id="bodyArea"]')
bodyArea
> bodyArea
{xml_nodeset (1)}
[1] <div id="bodyArea">\r\n<h4>市区町村別得票数</h4>\r\n\r\n\r\n<ul class="inline arrowlink">\n<li><a href="/senkyo/senkyo_s/data/shugiin46/s ...

a要素を抽出

次に,「北海道」から「沖縄県」までの各ページのURLには"shikuchouson"という言葉が入っているようなので,cssセレクタで「リンクタグ(<a></a>)でhrefに"shikuchouson"を含むもの」という意味のa[href *= 'shikuchouson']という条件を指定して抽出します。

f:id:nigimitama:20181218162050p:plain

a_tags = bodyArea %>% html_nodes(css = "a[href *= 'shikuchouson']")
a_tags
> a_tags
{xml_nodeset (47)}
 [1] <a href="/senkyo/senkyo_s/data/shugiin46/shikuchouson_01.html">北海道</a>
 [2] <a href="/senkyo/senkyo_s/data/shugiin46/shikuchouson_02.html">青森県</a>
(中略)
...

URLを抽出

html_attr()を使ってaタグのhref属性に指定されている文字列を取得し,各ページへのURLを取得します

# html_attr():タグの属性に指定されている文字列を取得
urls = a_tags %>% html_attr("href")
urls
> urls
 [1] "/senkyo/senkyo_s/data/shugiin46/shikuchouson_01.html" "/senkyo/senkyo_s/data/shugiin46/shikuchouson_02.html"
 [3] "/senkyo/senkyo_s/data/shugiin46/shikuchouson_03.html" "/senkyo/senkyo_s/data/shugiin46/shikuchouson_04.html"
(以下略)

リンクテキストを抽出

html_text()を使ってタグのテキスト部の文字列を取得します。

texts = a_tags %>% html_text()
texts
> texts
 [1] "北海道"   "青森県"   "岩手県"   "宮城県"   "秋田県"   "山形県"   "福島県"   "茨城県"   "栃木県"   "群馬県"   "埼玉県"   "千葉県"  
[13] "東京都"   "神奈川県" "新潟県"   "富山県"   "石川県"   "福井県"   "山梨県"   "長野県"   "岐阜県"   "静岡県"   "愛知県"   "三重県"
(以下略)  

ループ処理

以上の基礎知識を組み合わせ,47都道府県分をループするように処理を組みます。

前節で47都道府県分の各県のページへのURLとテキストを入手したので,あとはその先のページでhrefに"xls"を含む要素のURLを抽出したりすればいいだけです。

# 47都道府県分ループして各ページのURLを取得する
for(i in 1:length(urls)){
  html_i = read_html(str_c("http://www.soumu.go.jp",urls[i]))
  
  ## i県のxls(小選挙区,比例代表の2つのデータ)のリンク
  urls_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_attr("href")
  
  ## i県のxlsのリンクの文字列(説明文)
  texts_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_text()
  
  ## data frameにする
  url_df_i = data_frame(url = urls_i, text = texts_i, prefName = texts[i])
  if(i == 1) url_df = url_df_i
  if(i != 1) url_df = bind_rows(url_df, url_df_i)
  
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}
url_df %>% head()
> url_df %>% head()
# A tibble: 6 x 3
  url                         text     prefName
  <chr>                       <chr>    <chr>   
1 /main_content/000205177.xls 小選挙区 北海道  
2 /main_content/000205178.xls 比例代表 北海道  
3 /main_content/000205179.xls 小選挙区 青森県  
4 /main_content/000205180.xls 比例代表 青森県  
5 /main_content/000205181.xls 小選挙区 岩手県  
6 /main_content/000205182.xls 比例代表 岩手県  

各xlsファイルのURLと名称に関するテキストが取得できました。

②xlsファイルのダウンロード

3.すべてのxlsファイルをダウンロード

に取り組みます。

download.file(url, destfile)で指定したURLのファイルを,指定したパス(ファイル名)で保存します。

# 取得したURL一覧から順次xlsをダウンロードする
base_url = "http://www.soumu.go.jp/" # URLの前半部分(取得したURLが相対URLなので)
# ダウンロード先フォルダの作成
download_folder = str_c("./xls") # ダウンロード先フォルダ
dir.create(download_folder) # 「xls」というフォルダを作成


# download.file():「指定したURL」から「指定したパス」へのダウンロード
## ファイル名ベクトルの作成:「北海道_小選挙区」型のファイル名
file_name = str_c(url_df[["prefName"]],"_",url_df[["text"]])
## URLベクトルの作成
url = str_c(base_url,"/",url_df[["url"]])
## 各URLをダウンロード
for(i in 1:length(url)){
  download.file(url = url[i],
                destfile = str_c(download_folder,"/",file_name[i],".xls"),
                mode = "wb") # 今回は"wb"がうまくいった
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}

# ダウンロードしたファイルたち
list.files(download_folder)
> list.files(download_folder)
 [1] "愛知県_小選挙区.xls"   "愛知県_比例代表.xls"   "愛媛県_小選挙区.xls"   "愛媛県_比例代表.xls"   "茨城県_小選挙区.xls"  
 [6] "茨城県_比例代表.xls"   "岡山県_小選挙区.xls"   "岡山県_比例代表.xls"   "沖縄県_小選挙区.xls"   "沖縄県_比例代表.xls"
(以下略)

ダウンロードしたxlsファイルは{readxl}で前処理していきます(この部分は長くなるので書きませんが)。

まとめ

上記の処理をまとめると,以下のようになります。

# ライブラリ読み込み
library(tidyverse)
library(readr)
library(rvest)

# 1. スクレイピングしてURLを取得 ===========================
# read_html():htmlの読み込み
html_data = read_html("http://www.soumu.go.jp/senkyo/senkyo_s/data/shugiin46/shikuchouson.html")

# html_nodes():cssやxpathを指定して目的のタグを抽出
bodyArea = html_data %>% html_nodes(xpath = '//*[@id="bodyArea"]')
a_tags = bodyArea %>% html_nodes(css = "a[href *= 'shikuchouson']")

# html_attr():タグの属性に指定されている文字列を取得
urls = a_tags %>% html_attr("href")
# html_text():タグのテキスト部の文字列を取得
texts = a_tags %>% html_text()


# 47都道府県分ループして各ページのURLを取得する ------------
for(i in 1:length(urls)){
  html_i = read_html(str_c("http://www.soumu.go.jp",urls[i]))
  
  ## i県のxls(小選挙区,比例代表の2つのデータ)のリンク
  urls_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_attr("href")
  
  ## i県のxlsのリンクの文字列(説明文)
  texts_i = html_i %>% html_nodes(css = ".linklist") %>% 
    html_nodes(css = "a[href *= 'xls']") %>% html_text()
  
  ## data frameにする
  url_df_i = data_frame(url = urls_i, text = texts_i, prefName = texts[i])
  if(i == 1) url_df = url_df_i
  if(i != 1) url_df = bind_rows(url_df, url_df_i)
  
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}


# 2. ダウンロード ==========================================
# 取得したURL一覧から順次xlsをダウンロードする
base_url = "http://www.soumu.go.jp/" # URLの前半部分(取得したURLが相対URLなので)
# ダウンロード先フォルダの作成
download_folder = str_c("./xls") # ダウンロード先フォルダ
dir.create(download_folder) # 「xls」というフォルダを作成


# download.file():「指定したURL」から「指定したパス」へのダウンロード
## ファイル名ベクトルの作成:「北海道_小選挙区」型のファイル名
file_name = str_c(url_df[["prefName"]],"_",url_df[["text"]])
## URLベクトルの作成
url = str_c(base_url,"/",url_df[["url"]])
## 各URLをダウンロード
for(i in 1:length(url)){
  download.file(url = url[i],
                destfile = str_c(download_folder,"/",file_name[i],".xls"),
                mode = "wb") # 今回は"wb"がうまくいった
  # 1ループごとに0.5秒待ってサーバーへの負荷をへらす
  Sys.sleep(0.5) 
}

# ダウンロードしたファイルたち
list.files(download_folder)

参考文献

Rによるスクレイピング入門

Rによるスクレイピング入門

石田さんのこの本は基礎的なことから幅広く書いてあってスクレイピングするとき手元にあるとすごく良いです。