見出し画像

プロ野球選手のプロフィールデータをいっきに10年分取得してみた

前回プロ野球選手の成績データを10年分スクレイピングしました。
前回スクレイピングしたデータセットは主に成績データになります。
プロ野球選手のデータには推定年俸や身長、体重、出身、年齢といったプロフィールデータもあると何か特徴を捉える助けになるかもしれません。
そこで今回は、プロ野球選手のプロフィールデータを10年分、全チームスクレイピングします。

前回のプロ野球選手成績データの記事は以下になります。
こちらの記事では成績データを10年分をとるための方法が記載されていますので興味ある方はぜひ!


今回スクレイピングさせていただくサイト

いつも通り以下のサイトです。
いつもありがとうございます。
プロ野球freakさんです。
こちらにプロ野球選手の年齢、推定年俸、身長、体重、出身といったデータがあります。
ここからデータをスクレイピングしていきます。

コードの全体

コードはスクレイピングの関数とメイン関数の2種類の構成になります。
スクレイピング関数では取ってきたデータをデータフレームに変換して、データを加工して返します。
メイン関数はURLの要素をリストで持ち、繰り返し処理でURLを作成していき、スクレイピング関数にURLをスクレイピングを行い、得られたデータを最後に保存する流れになります。

import pandas as pd
import time

def ScrapingProfileData(url,team_initial,year):
   """プロフィールデータをスクレイピングする関数"""
   data = pd.read_html(url)
   data = data[0]
   data['年俸(推定)'] = data['年俸(推定)'].str.replace('万円','')
   data['年俸(推定)'] = data['年俸(推定)'].str.replace(',','')
   data['身長'] = data['身長'].str.replace('cm','')
   data['年数'] = data['年数'].str.replace('年','')
   data['体重'] = data['体重'].str.replace('kg','')
   data['年齢'] = data['年齢'].str.replace('歳','')
   data['BMI'] = data['体重'].astype('float')/data['身長'].astype('float')**2*10000
   data['チーム']=team_initial
   data['年']=year
   return data
   
if __name__ == "__main__":
   profile_df=pd.DataFrame()
   #urlのパターンがチームのイニシャルになっていますので、リストを作っていきます
   name_list = ['g','yb','t','c','d','s','l','h','e','m','f','bs']
   year_list=['09','10','11','12','13','14','15','16','17','18','19','']
   #基礎となるurlを用意します。
   url_base = 'https://baseball-data.com/'
   #基礎となるurlに各チームのページに移れるようにname_listの文字をくっつけてurlを作ります。
   for team_initial in name_list:
       for year in year_list:
           try:
               url = url_base + year +'/player/'+ str(team_initial) +'/'
               print(url)
               data = ScrapingProfileData(url,team_initial,year)
               #不正アクセスと間違えられないために間隔をあけましょう。
               profile_df = pd.concat([profile_df,data])
               time.sleep(3)
           except:
               print('error')
   print('scraping_end')
   profile_df.to_csv('scrapingProfile.csv',index=False)


メインコードの説明

メイン関数は主にURLをどう作っていけば、自動でスクレイピングできるかが重要です。
今回スクレイピングするサイトのURLの構成はチームはイニシャルで構成されていて、年度は下2桁の数字になっています。20年の最新だけ何もないので空のデータを入れておきます。
このリストの文字列を組み合わせす自動処理でスクレイピングを行います。
細かな処理は1行ずつ追っていきます。

if __name__ == "__main__":
   profile_df=pd.DataFrame()
   #urlのパターンがチームのイニシャルになっていますので、リストを作っていきます
   name_list = ['g','yb','t','c','d','s','l','h','e','m','f','bs']
   year_list=['09','10','11','12','13','14','15','16','17','18','19','']
   #基礎となるurlを用意します。
   url_base = 'https://baseball-data.com/'
   #基礎となるurlに各チームのページに移れるようにname_listの文字をくっつけてurlを作ります。
   for team_initial in name_list:
       for year in year_list:
           try:
               url = url_base + year +'/player/'+ str(team_initial) +'/'
               print(url)
               data = ScrapingProfileData(url,team_initial,year)
               #不正アクセスと間違えられないために間隔をあけましょう。
               profile_df = pd.concat([profile_df,data])
               time.sleep(3)
           except:
               print('error')
   print('scraping_end')
   profile_df.to_csv('scrapingProfile.csv',index=False)

1行目はmain文です。
jupyter notebookではあまり意味のない構文になりますが、pythonファイルで事項する場合、外部ファイルの呼び出しではないときのコマンドラインからの呼び出しの場合は、まずメイン文の関数から処理実行が始まります。

if __name__ == "__main__":

スクレイピングしたデータをまとめて格納しておくためのデータフレームを用意します。
ここでは、profile_dataにデータフレーム型の空のデータを格納しています。

profile_df=pd.DataFrame()

URLを構成する文字列をリストに格納します。
name_listに12球団のチームそれぞれのイニシャルを格納しています。
year_listには年度ごとで管理されているページにアクセスするために、下2桁の年数を格納します、
当年度はページが年度の下二桁が入ってないので空で持ちます。

name_list = ['g','yb','t','c','d','s','l','h','e','m','f','bs']
year_list=['09','10','11','12','13','14','15','16','17','18','19','']

今回スクレイピングするサイトの基となるURLです。
このURLにスクレイピングしたいページにいくためのURLを作成していきます。
url_baseに基礎の変わらない部分のURLを格納します。

url_base = 'https://baseball-data.com/'

繰り返し処理を行っていきます。
for文でname_listとyear_listの要素を取り出す形で繰り返し処理を行っていきます。
URLが万が一なかった場合にエラーで処理が止まらないように例外処理を入れるためにtryを使います。

   for team_initial in name_list:
       for year in year_list:
           try:

urlにリストから取り出した球団を識別する文字列tam_initialと年度を識別する文字列yearを組み合わせてアクセスするURLを格納します。
スクレイピングできるサイトはURLの構造がしっかりしていることが条件に挙げられます。
urlにアクセスするURLを作りましたら、確認のためprint文で表示します。

url = url_base + year +'/player/'+ str(team_initial) +'/'
print(url)

スクレイピングする関数にURL、球団のイニシャルのteam_initial、年度のyearを渡して、結果をdataに格納します。

data = ScrapingProfileData(url,team_initial,year)

スクレイピングで得られたデータをprofile_dfに結合していきます。

profile_df = pd.concat([profile_df,data])

3秒間処理を止めます。
スクレイピングで高速にアクセスしてしまうことによる行為を避けるために明示的に意図的に処理を止めます。

time.sleep(3)

例外処理でエラーが出た場合は、print文でerrorと出力します。

except:
   print('error')

スクレイピングが終わったら、print文でscraping endと出力して終わります。

print('scraping_end')

最後に取り終わったデータをcsvに保存します。

profile_df.to_csv('scrapingProfile.csv',index=False)

スクレイピング関数の説明

データに記載されている単位などのデータをクリーニングします。
チーム列を新しく作りのイニシャルを格納します。
また「年」のデータを追加するため「年」の列を新しく作成し、データを格納します。
おまけにBMIのデータを算出しています。
最後に加工したデータフレームを返します。

def ScrapingProfileData(url,team_initial,year):
   """プロフィールデータをスクレイピングする関数"""
   data = pd.read_html(url)
   data = data[0]
   data['年俸(推定)'] = data['年俸(推定)'].str.replace('万円','')
   data['年俸(推定)'] = data['年俸(推定)'].str.replace(',','')
   data['身長'] = data['身長'].str.replace('cm','')
   data['年数'] = data['年数'].str.replace('年','')
   data['体重'] = data['体重'].str.replace('kg','')
   data['年齢'] = data['年齢'].str.replace('歳','')
   data['BMI'] = data['体重'].astype('float')/data['身長'].astype('float')**2*10000
   data['チーム']=team_initial
   data['年']=year
   return data

関数の宣言文になります。
この関数ではurlにスクレイピングするURL、team_initialに球団のイニシャル、yearに年度を渡します。

def ScrapingProfileData(url,team_initial,year):

dataに取ってきたurl内のテーブル構造のデータを格納します。

data = pd.read_html(url)

dataにはリスト形式でテーブルごとに要素が入っています。
data[0]の要素に今回欲しいデータが入っているので、dataにdata[0]を代入しなおします。
この処理によりdataにデータフレーム型でデータが格納されます。

  data = data[0]

取ってきたデータには余分な文字列が入っていたりします。
例えば、年齢にはOO歳と「歳」という文字が入っています。
分析や計算するうえで入りませんので、このような文字を削除していきます。
そのほかの推定年俸などの項目にも含まれていますので、これらの文字列を一つずつ除去していきます。

   data['年俸(推定)'] = data['年俸(推定)'].str.replace('万円','')
   data['年俸(推定)'] = data['年俸(推定)'].str.replace(',','')
   data['身長'] = data['身長'].str.replace('cm','')
   data['年数'] = data['年数'].str.replace('年','')
   data['体重'] = data['体重'].str.replace('kg','')
   data['年齢'] = data['年齢'].str.replace('歳','')
   data['BMI'] = data['体重'].astype('float')/data['身長'].astype('float')**2*10000

BMIを計算して特徴量を追加します。
dataの型はどれもobject型と文字列の形ですので、数値に型変換しないと計算ができません。
そのため、astype('float')で実数型に変換してBMIの計算を可能にしています。

data['BMI'] = data['体重'].astype('float')/data['身長'].astype('float')**2*10000

スクレイピングしたデータがどこであるかはスクレイピングを繰り返してデータを一つのデータフレームに結合するとわからなくなってしまします。
どの年化も同様にわからなくなります。
そこで、チーム列と年列新たに追加して、その選手が何年のどこのチームに所属しているかがわかるように、URLのチームのイニシャルと年を格納してわかるようにします。

   data['チーム']=team_initial
   data['年']=year

最後にデータを返してこの関数は終了です。

   return data

まとめ

プロ野球選手のプロフィールデータを10年分スクレイピングして、csvに出力して保存する処理を紹介しました。
コードも数十行と意外と少ない行数のコードでデータが取れます。
スクレイピングするときの注意事項をしっかり守ってデータを抽出してデータ分析をしてみてください。

今回作成したコードとデータがダウンロードできます(有料)

ここから先は

124字 / 2ファイル
この記事のみ ¥ 200
期間限定 PayPay支払いすると抽選でお得に!

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