Web スクレイピング
HTML と CSS の基礎
- Web スクレイピングを行うためには、HTMLとCSSの基本的な知識が必要となる
HTML
- Web ページを表示するための言語
- HTML 自体はプログラミング言語ではない
理解すべき概念
- 要素
- タグ
- 改行や画像などを除き、始点・終点となるタグがそれぞれ存在
- 終点のタグには、中にスラッシュが入る
- 改行や画像などを除き、始点・終点となるタグがそれぞれ存在
- 属性
- id
- ID。1つのタグにつき1つ。重複が許されない
- class
- クラス名。複数設定可。重複OK
- alt (代替文字)
- href (リンク先)
- src (画像などのソース)
- id
- タグ
- 詳細な説明として、次のサイトも参考になる: https://developer.mozilla.org/ja/docs/Learn/Getting_started_with_the_web/HTML_basics
CSS
- HTMLをデザインするための言語
理解すべき概念
- 要素ごとにデザインを指定していく
- 指定の仕方
- ID
- id=“test”なら”#test”と指定
- class
- class=“test”なら”.test”と指定
- class=“test1 test2”なら”.test1.test2”と指定
- 間にスペースは不要
- class=“parent”の要素の中にある class=“child”の要素なら、“.parent .child”と指定
- 間に半角スペース
- 子要素、孫要素どちらにも適用
- class=“parent”の要素の中の直下にある class=“child”の要素なら、“.parent > .child”と指定
- 直属の子要素のみに適用
- タグ
- aタグ (リンク) なら “a” と指定
- タグ、class、idを併用して指定することも可能
- 例. “.parent .link a”, “#header .title span”
- ID
- 指定の仕方
- Web ページの HTML, CSS は、 ブラウザでF12 キーを押して表示される開発者ツールから確認可能
スクレイピングの注意点
- スクレイピングが禁止されている Web サイトでは実行してはならない
- 場合によっては処分されるかもしれない (大学のデータベースなどは特に注意)
- 集中的にアクセスしてはならない
- 一時的にサイトへアクセスしづらくさせ、一般利用者に迷惑をかける可能性がある
- そのような行為が継続された場合、サイト運営者がアクセス制限をかけることも
- アクセスするたびに 1 秒ほど操作を一時停止させるなどの対策を取る
スクレイピングの実行
- Webスクレイピング (= Webページからデータを収集する) には,rvest パッケージが便利
- rvest を読み込んでみる
pacman::p_load(rvest)- rvest::read_html(〜) のように, 関数名の前にライブラリ名を指定すれば,pacman::p_load()やlibrary()は不要
- 複数のライブラリで関数名が重複した際に使える方法
ページからデータを取得
- 東京都議会の会派別議員名簿のページを読み込む
data <- read_html("https://www.gikai.metro.tokyo.jp/membership/political-party.html")- 今回は tdタグ (=表のセル) だけ抽出し,HTMLからテキストに変換
member_name <- html_elements(data, "td") |>
html_text() # htmlをテキストに変換- どのタグを使えば良いか: Webページのソースから確認
- ページ上で右クリックし,「ページのソースを表示」をクリック
- タグ:
<html>〜</html>, <a>〜</a>などのこと- 基本的に1セット:始まりのタグ <〜> と終わりのタグ </ 〜> で挟むかたち
取得したデータを整理
- 奇数行が選挙区名,偶数行が議員名 -> データを2つに分ける
common <- tibble(value = member_name) |>
rownames_to_column() # 「行番号の列」を追加
distincts <- common |>
filter(rowOE == 1) # 選挙区名
names <- common |>
filter(rowOE == 0) # 議員名- 上記の2データを列として結合し,中身を確認
tokyo <- bind_cols(distinct = distincts$value, name = names$value)
View(tokyo) # データの表が新しいタブで閲覧できる議員データの取得
- 会派別議員名簿ページのリンクから飛べる各議員のページから, データを取得してみる
## とりあえず一番上に記載の議員のページを取得
data_person <- read_html(
"https://www.gikai.metro.tokyo.jp/membership/number002.html")
## 議員名はh3タグに含まれているので,h3タグの中身を取得し,HTMLからテキストに変換
person_name <- data_person |>
html_elements("h3") |>
html_text()
## 議員プロフィールはliタグ (複数) に含まれているので,
## 全てのliタグの中身を取得し,HTMLからテキストに変換
person_profs <- data_person |>
html_elements("li")- 余計なタグがあるので取り除く
person_prof # 取得したliタグの一覧を確認
## 余計なliタグが含まれている模様!
person_prof <- numeric()
## 12番目のliタグから 最後のliタグまで が欲しい情報 ← これを取得する
for(i in 12:length(person_profs)){
person_prof[i-11] <- person_profs[i] |> html_text()
}議員データの前処理
- 取得できた文字列を整えるために、stringrパッケージの関数を用いる
person_prof # 取得できたデータを確認
# person_prof[1] # 氏名
# person_prof[2] # 会派
# person_prof[3] # 期数
# person_prof[4] # 委員会
# person_prof[5] # 事務所?の住所
# person_prof[6] # TEL
# person_prof[7] # 選挙区
# person_prof[8] # WebサイトURL
person_prof[3] <- str_sub(person_prof[3], start = 3, end = 3) # 期数抜き出し
person_prof[6] <- str_sub(person_prof[6], start = 4, end = -1) # TEL抜き出し
person_prof[7] <- str_sub(person_prof[7], start = 5, end = -2) # 選挙区抜き出し
person_prof # データが整序された!データセットの作成
- 今までの作業を関数としてまとめる
member_data <- tibble()
getMemberData <- function(url){
data_person <- read_html(url)
person_name <- data_person |> html_elements("h3") |> html_text()
person_profs <- data_person |> html_elements("li")
person_prof <- numeric()
for(i in 12:length(person_profs)){
person_prof[i-11] <- person_profs[i] |> html_text()
}
person_prof[3] <- str_sub(person_prof[3], start = 3, end = 3) # 期数抜き出し
person_prof[6] <- str_sub(person_prof[6], start = 4, end = -1) # TEL抜き出し
person_prof[7] <- str_sub(person_prof[7], start = 5, end = -2) # 選挙区抜き出し
for(i in 1:8){
return(tibble(person_prof))
}
} - 議員個人ページへアクセスしてデータを取得していくため, 各ページへのリンクのアドレスを取得し,議員名簿データに挿入する
member_link <- html_elements(data, "a") |> # aタグのデータを全て抽出
html_attr("href") # aタグのhref属性 (<- URLが格納されてある) の中身だけ抽出
member_links <- numeric() # for文の結果を格納するオブジェクトを作成
## URLは相対パスで保存されていたので,前のURLと結合した絶対パスへ変換
for(i in 16:140){
member_links[i-15] <- paste0("https://www.gikai.metro.tokyo.jp/membership/",
member_link[i])
}
## for文で出力されたオブジェクトを,linkという列名で,tokyoデータに結合
tokyo <- bind_cols(tokyo, link = member_links) - 関数を実行し,データセットを完成させる
## データの一斉取得
## 一気に大量にアクセスすると,アクセス先のサーバに負荷がかかるため注意する
for (i in 1:10){ ## 今回は上から10人分のみを実施
assign(paste0("member_", i), getMemberData(tokyo$link[i]))
Sys.sleep(1) # アクセス集中防止のため、1 アクセスごとに 1 秒待機させる
}
## データをtibbleに格納
tokyo2 <- tibble() # for文の結果を格納する空のオブジェクトを作成
for(i in 1:10) {
for(j in 1:8){
tokyo2[i, j] <- get(paste0("member_", i))[j, 1]
}
}
## 列名を指定
tokyo2 <- tokyo2 |>
setNames(c("氏名", "会派", "期数", "委員会", "住所", "TEL", "選挙区", "URL"))