見出し画像

40行で13年の全球団の動員数をスクレイピングする

プロ野球選手の成績をよくとっていますが、選手のデータだけがデータじゃない!(ぇ
今回は各球団の球場の来場者数をスクレイピングします。
※スクレイピングを行うので過剰なアクセスは気をつけましょう

今回のプログラムは主に3つの部分に分かれます。
ライブラリインポートでは今回使うライブラリのインポートをします。
スクレイピング関数では、アクセスしたURLで取ってきたデータを加工する処理を関数にまとめています。
メイン処理では、各チーム、各年ごとURLが分かれているので、URLを文字列処理で作成しながらループ処理でスクレイピングを行い、データを球団ごとに2009年から2021年の球場来場者数データを保存します。

スクレイピングするサイト

今回もお世話になるサイトはプロ野球フリークさんです。

ライブラリインポート

データ加工用にpandas、時間計測ようにtime、文字列処理などで警告が多く出るため警告を出さないためのwarningsをインポートします。
警告は普段は何らかの注意点を教えてくれるので、常にignoreはお勧めできませんが今回は文字列処理での警告がループ処理の旅に出るので消しておきます。

import pandas as pd
import matplotlib.pyplot as plt
import time as t
import warnings
warnings.simplefilter("ignore")
%matplotlib inline

スクレイピング関数

スクレイピング関数では、スクレイピングするURLと年ごとに分かれているページにアクセスするための年を引数にしています。
dataにpd.read_html(url)でまずサイトのデータを取ります。
今回は来場者数のみ取りたいので、df3=df2[['日付','観客数']]でその日の観客数のみ取ってきます。
   df3['観客数'] = df3['観客数'].str.replace(',','')
   df3['観客数'] = df3['観客数'].str.replace('人','')
   df3['観客数'] = df3['観客数'].str.strip()
では、サイトを見ればわかりますが、桁数区切りにカンマ、「人」がデータに入っているのでreplaceメソッドで取り除きます。
dropnaでNULLのデータを取り除きます。
日付は曜日まで(月)のように入っておりますので、df['日付'].str[:-3]で曜日部分を除きます。
df3['日付'] = pd.to_datetime(year + '年' + df3['日付'], format='%Y年%m月%d日')で文字列型を日付型に変換します。
最後にdf3['観客数'] = df3['観客数'].astype(int)で文字列型から観客数をint型にして終了です。

def scrape(url,year):
   data = pd.read_html(url)
   df = data[2]
   df3 = df2[['日付','観客数']]
   df3['観客数'] = df3['観客数'].str.replace(',','')
   df3['観客数'] = df3['観客数'].str.replace('人','')
   df3['観客数'] = df3['観客数'].str.strip()
   df3.dropna(inplace=True)
   df3['日付'] = df3['日付'].str[:-3]
   df3['日付'] = pd.to_datetime(year + '年' + df3['日付'], format='%Y年%m月%d日')
   df3['観客数'] = df3['観客数'].astype(int)
   return df3

メイン処理

メイン処理ではスクレイピング関数を使って、アクセスして取ってきたデータを加工し、最初に初期化して用意しておいたデータフレーム型にデータを追加していきます。
球団ごとにデータを保存していくようにします。

start = t.time()は時間計測のための開始時刻の取得です。
teamsに各球団を表すURLの一部を格納しておきます。
for文でteamsリスト内の球団のURLを取得してループ処理をしていきます。
data = pd.DataFrame()で取得したデータをまとめていくためのデータフレームを初期化します。

ループ処理は2重になっていて内側のループは「年」を回します。
2009年から2021年のデータを取るため、9~22のループにしています。
s = '{:02}'.format(year)で年の下2桁を文字列として代入します。
print('https://baseball-freak.com/audience/'+ s + '/'+team+'.html')ではアクセスするURLを表示しています。(この処理はなくても良いです。)
df = scrape('https://baseball-freak.com/audience/'+ s + '/baystars.html','20'+s)でアクセスしたURLをスクレイピングして、結果をデータフレームに格納します。
df['球団'] = teamで球団列を新たに作成します。
data = pd.concat([data,df])で取得したデータを追加していきます。
t.sleep(5)は処理を5秒止めます。
意図的に止めない場合処理が速く、短時間にサイトへアクセスしてしまうため意図的に処理を止めてサイトに負荷をかけない配慮をします。

data.to_csv('audience_data/' + team + '_audience.csv',index=False)
で球団ごとデータをcsv形式で保存します。

   elapsed_time = t.time() - start
   print ("elapsed_time:{0:.3f}".format(elapsed_time) + "[sec]")
で処理時間を表示します。

if __name__ == "__main__":
   start = t.time()
   teams = ['swallows','baysters','tigers','giants','carp','dragons','buffaloes','marines','eagles','hawks','fighters','lions']

   for team in teams:
       data = pd.DataFrame()
       for year in range(9,22):
           s = '{:02}'.format(year)
           print('https://baseball-freak.com/audience/'+ s + '/'+team+'.html')
           df = scrape('https://baseball-freak.com/audience/'+ s + '/baystars.html','20'+s)
           df['球団'] = team
           data = pd.concat([data,df])
           t.sleep(5)

       data.to_csv('audience_data/' + team + '_audience.csv',index=False)

   elapsed_time = t.time() - start
   print ("elapsed_time:{0:.3f}".format(elapsed_time) + "[sec]")

結果は以下のような感じになります。

スクリーンショット 2022-09-09 1.10.02
スクリーンショット 2022-09-09 1.10.18

5秒間の処理ストップを入れているので全球団取得に14分弱かかっています。

今回の記事のプログラムとデータはここからダウンロードできます。(有料)

ここから先は

116字 / 3ファイル
この記事のみ ¥ 200

よろしければサポートをよろしくお願いします。サポートいただいた資金は活動費に使わせていただきます。