スクレイピングでNPBの年間観客動員数を取得する【R】

NPBの年間観客動員数は2013年から増加しているらしい*1。もう少し細かいデータを見てみたいなと思ったのでスクレイピングNPBの年間観客動員数をRを使って取得して、前処理までしたので記事にしてみました。

今回スクレイピング対象のページはこちらです。

※ Jupyter notebookで行っているためもしかしたらRstudioだとエラーが発生するところがあるかもしれません

必要なライブラリの読み込み

library(tidyverse)
library(rvest)
library(glue)
library(scales)

tidyverse

データサイエンス用に設計されたパッケージ群です。データの読み込み・抽出・加工・可視化など、データサイエンスの基本的なパッケージ群を読み込むことができます。tidyverseの登場によってRの前処理が簡単になったとか。

rvest

Webスクレイピングに必要なパッケージです。これのおかげで簡単にスクレイピングができます。

glue

Pythonでいうf文字列を実現するために使用します。今回は年度毎に年間観客動員数が載っているPathが異なります。なので、年度毎にPathを生成する必要があるためglueを使用します。

scales

ちょっと自信がないですが、グラフを作成する際に使用するパッケージのようです*2。特に軸とか凡例とかが対象なのかも。今回は棒グラフを作成する際に、scales::label_commaを用いて3桁毎にカンマを打つために使用します。

指数表記の回避

options(scipen=10)

Rはデフォルトだと指数表記になるようなので上記を実行します。scipenの数字を大きくすると桁数が大きくなっても指数表記されないようです。数字は桁数を表していると思うのですが、詳しい内容まではわかりませんでした(ドキュメントはこちら)。

スクレイピングで年度毎の観客動員数を取得する

lst_year  <- c("09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20")

for (year in lst_year){
       
    # スクレイピング対象のページに対して、アクセス負荷をかけないために5秒あける
    Sys.sleep(3)
    
    # パスの取得
    path <- glue("https://baseball-freak.com/audience/{year}/")
    
    # 対象のページの取得
    d <- read_html(path)
    
    # 対象のページのテーブルタグを取得
    dfs <- html_table(d)
    
    # 最初のリスト(目的のテーブル)を取得
    df_audience  <-  dfs[[1]]
    
    # 年度のカラムを追加
    df_audience$年度  <- as.integer(glue("20{year}"))
    
    # 1試合平均のカラムの名前を平均に変更
    # 変更する理由はmutateで文字列からintに変更するときにカラム名を指定できるようにするため
    # カラムの一番最初が数字だとエラーが起こる
    df_audience <- dplyr::select(df_audience, 1, 平均 = 2, 3, 4, 5)
    
    # 平均・試合数・合計のカラムを文字列からintへ変更
    # 具体的には各カラムの数字意外を消して(正確には""に置き換えて)intへ変更
    df <- dplyr::mutate(df_audience,
                     平均=as.integer(gsub
                                   (平均,pattern="\\D",replacement = "", ignore.case = TRUE)
                                  ),
                     試合数=as.integer(gsub
                                    (試合数,pattern="\\D",replacement = "", ignore.case = TRUE)
                                   ),
                     合計=as.integer(gsub
                                   (合計,pattern="\\D",replacement = "", ignore.case = TRUE)
                                  )
                     )
    
    # ifを使って最初(2009年)はdfをdf_mainにして、あとはdf_mainに対してdfを縦にマージする
    if (year == "09"){
        df_main <- df
    } else {
        # df_mainに対してdfを縦にマージする
        df_main <- rbind(df_main, df)
    }
}

説明はコメントを読んでいただけると嬉しいですが、ざっくり説明すると、read_html()で対象のパスのデータを取得しているようです。html_table()では、取得したデータのうちテーブルタグのデータを取得していて、それをdfsの変数に入れています。そのうち一番最初のテーブルに年間観客動員数が入っているのでdfs[[1]]をdf_audience に入れています。あとは前処理として数字にしたいものをINTにしたりしています。最後にif文のところで毎年のデータを縦につなげていきます。

df_NPBカラム名がチームの行だけを抽出する

df_NPB <- df_main %>% 
    # filterで条件を絞る
    filter(チーム == "合計")

今回取得したデータには各球団の年間観客動員数も入っている。なので、チームが「合計」のものだけを取得するためにfilter()を使用する。filterしたデータはパイプ演算子を使って変数df_NPBに入れる。これでdf_NPBの合計カラムには年間の観客動員数数のみが入っていることになる。

これでNPBの年間観客動員数の取得が完了!目的が達成できました!

おまけ

おまけで年間観客動員数をグラフ化したいと思います。

年度カラムを文字列に変換

# 棒グラフを作成する際にX軸にすべての年度が表示されるようにするためにINTから文字列に型変換
# 棒グラフでX軸がINTだとすべては表示されない
df_NPB$年度 <- as.factor(df_NPB$年度)

初めから年度カラムは文字列にすればよいんだけど、何となく年度はINTにしたくて、グラフを作成する前に文字列に変換するという工程にしました。まぁほかのところでdf_mainを使用する可能性を考えるとこれで良いのかもしれない。文字列にする理由はコメントにもありますが、棒グラフを作成するときにINTだと全部の年度が載らないので文字列にしました。X軸をカテゴリとして扱う感じなのかなと思っています。

グラフ化

g <- ggplot(df_NPB, aes(x=年度, y=合計)) +
    geom_bar(stat = "identity") +
    # 3桁カンマにするため
    scale_y_continuous(labels = label_comma()) +
    ggtitle("NPB年間観客動員数") +
    # タイトルをセンターの位置にする
    theme(plot.title = element_text(hjust = 0.5)) +
    ylab("観客動員数(人)") +
    xlab("西暦")
plot(g)

年度別のNPB年間観客動員数
年度別のNPB年間観客動員数