見出し画像

2020年のオープン戦も終わったので今年のオープン戦の野手、投手成績のデータを収集する

2020年のプロ野球の開幕も決まり、オープン戦も先週終わりました。
さっそく今年のオープン戦の成績を入手して分析をしようと思います。
前の記事にプロ野球の成績データのスクレイピングを作ってあるので関数を使いまわせば簡単に取れるでしょう!

と、思ったのが甘かった・・・

微妙にデータの項目や構造が違ったので全く使えない・・・
スクレイピングではよくあることです。
全く同じ構造をしているホームページのほうが少ないですからねw

そこで今回は、プロ野球の公式サイトからオープン戦成績のデータをスクレイピングをします。

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

今回はプロ野球の公式サイトからデータをいただこうと思います。
スクレイピングするときはサイトに負荷をかけないように気を付けてください。


スクレイピングコード

スクレイピングのコードを以下に記載します。
関数自体は10行以下。
メイン関数も30行程度です。

import pandas as pd
import numpy as np
import time

def scrapingPitcherOpen(url):
   """
   2020年オープン戦データ投手の収集用関数
   
   """
   data = pd.read_html(url)
   data = data[0]
   data.fillna('-',inplace=True)
   data = data[2:]
   data.reset_index(inplace=True,drop=True)
   data.columns=['利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率']
   data.drop('-',axis=1,inplace=True)
   return data

def scrapingHitterOpen(url):
   """
   2020年オープン戦データ野手の収集用関数
   """
   data = pd.read_html(url)
   data = data[0]
   data.fillna('-',inplace=True)
   data = data[2:]
   data.reset_index(inplace=True,drop=True)
   data.columns=data.columns=['利き打', '選手', '試合', '打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
      '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球', '故意四', '死球', '三振',
      '併殺打', '打率', '長打率', '出塁率']
   return data

if __name__ == "__main__":
   #チームリスト
   team_list=['l','h','e','f','b','g','db','t','c','d','s','m']
   
    #データフレーム初期化    df_Pitcher = pd.DataFrame(columns=['チーム','利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率'])
   df_Batter = pd.DataFrame(columns=['チーム','利き打', '選手', '試合', '打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
      '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球', '故意四', '死球', '三振',
      '併殺打', '打率', '長打率', '出塁率'])
   
   for i in team_list:
       url_p = 'http://npb.jp/bis/2020/stats/idp1op_'+str(i)+'.html'
       url_b = 'http://npb.jp/bis/2020/stats/idb1op_'+str(i)+'.html'
       
       print('now scraping:'+url_p)
       data_p = scrapingPitcherOpen(url_p)
       data_p['チーム']=str(i)
       time.sleep(1)

       print('now scraping:'+url_b)
       data_h = scrapingHitterOpen(url_b)
       data_h['チーム']=str(i)
       time.sleep(1)
       
       df_Pitcher=pd.concat([df_Pitcher,data_p])
       df_Batter=pd.concat([df_Batter,data_h])
       
   df_Pitcher.reset_index(inplace=True,drop=True)
   df_Batter.reset_index(inplace=True,drop=True)

   df_Pitcher.to_csv('Open_Pitch.csv',index=False)
   df_Batter.to_csv('Open_Hitter.csv',index=False)


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

コードの詳細について説明していきます。
まずは投手の成績をスクレイピングする関数についてです。
関数自体は10行程度です。
大まかな流れとしては、
1.サイトのデータを取る
2.データフレームにする
3.NULLを'-'(ハイフン)で埋める
4.いらない上から2行を捨てる
5.indexを振りなおす
6.カラム名を整理
7.いらないカラムを捨てます
8.最後はデータフレームで返します


def scrapingPitcherOpen(url):
   """
   2020年オープン戦データ投手の収集用関数
   """
   data = pd.read_html(url)
   data = data[0]
   data.fillna('-',inplace=True)
   data = data[2:]
   data.reset_index(inplace=True,drop=True)
   data.columns=['利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率']
   data.drop('-',axis=1,inplace=True)
   return data

1行ずつ追っていきましょう。
まずはサイト内のデータを取ってくる処理です。
read_htmlをするとサイト内のテーブル構造しているデータを取ってきてくれます。
テーブル出ない形にされているとデータを取ってこれないという欠点はありますが、取れればリストの形でデータが取得できます。
そして、簡単にデータフレームにできるので、HTML構造のどのタグにどのてーだが入っていてどのようにデータを格納していくかといったことを考えずに済むので簡単にデータが取れます。

data = pd.read_html(url)

次にdataに格納されたデータはリストの形式です。
データを加工しやすいデータフレーム形式に変えるため、リストの要素を取り出す形で代入します。
そうするとリスト内の要素はデータフレームとしてデータが格納されてるのでデータフレームになります。

data = data[0]

データフレームの型にしましたら、NULLを適当な値で埋めておきます。

data.fillna('-',inplace=True)

最初の2行がいらないデータになっていますので除外します。

data = data[2:]

indexの数字が上2行を削った分、2からスタートになっていますのでindexを振りなおします。
別にこの処理なくてもいいので、必要なければなくても問題ないです。

data.reset_index(inplace=True,drop=True)

次にカラム名が一部合っていないので、以下のようにサイトを見てカラム名を揃えます。

data.columns=['利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率']

ピッチャーは投球回の部分がサイトのテーブル構造上、小数点と整数部分が別の列になっています。
今回は列をくっつけて一つの列にするのも面倒ですので、小数点部分は切り捨てにします。(ぉぃ)
小数点部分は使わないので、dropで落とします。

data.drop('-',axis=1,inplace=True)

最後にデータフレームとして返します。

return data

意外と処理が少なく簡単にデータが取れたのではないでしょうか?

野手のスクレイピングも同じ処理です。
違う個所は投球回の列削除がないこととカラム名です。
野手のオープン戦データのスクレイピングのほうが処理が一つ少ないです。
カラム名は以下になります。

data.columns=data.columns=['利き打', '選手', '試合', '打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
      '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球', '故意四', '死球', '三振',
      '併殺打', '打率', '長打率', '出塁率']

メイン関数の説明

メイン関数の説明になります。
行うことは、スクレイピングするURLと各チームのURLにアクセスできるようにリストを作成します。
格納用のデータフレームを投手、野手用ともに用意して、for文で各チームのオープン戦成績データがあるサイトに先ほどのスクレイピング関数を使用してデータを取ります。
取ってきたデータはその都度格納用のデータフレームに付け足していきます。
最後にデータを保存してデータの収集は完了になります。

if __name__ == "__main__":
   #チームリスト
   team_list=['l','h','e','f','b','g','db','t','c','d','s','m']
   
    #データフレーム初期化    df_Pitcher = pd.DataFrame(columns=['チーム','利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率'])
   df_Batter = pd.DataFrame(columns=['チーム','利き打', '選手', '試合', '打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
      '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球', '故意四', '死球', '三振',
      '併殺打', '打率', '長打率', '出塁率'])
   
   for i in team_list:
       url_p = 'http://npb.jp/bis/2020/stats/idp1op_'+str(i)+'.html'
       url_b = 'http://npb.jp/bis/2020/stats/idb1op_'+str(i)+'.html'
       
       print('now scraping:'+url_p)
       data_p = scrapingPitcherOpen(url_p)
       data_p['チーム']=str(i)
       time.sleep(1)

       print('now scraping:'+url_b)
       data_h = scrapingHitterOpen(url_b)
       data_h['チーム']=str(i)
       time.sleep(1)
       
       df_Pitcher=pd.concat([df_Pitcher,data_p])
       df_Batter=pd.concat([df_Batter,data_h])
       
   df_Pitcher.reset_index(inplace=True,drop=True)
   df_Batter.reset_index(inplace=True,drop=True)

   df_Pitcher.to_csv('Open_Pitch.csv',index=False)
   df_Batter.to_csv('Open_Hitter.csv',index=False)

まず一行目のmain関数はpythonの場合、コマンドラインから実行するとmain分があれば、そこの関数内の処理を実行していきます。

if __name__ == "__main__":

スクレイピングするサイトの構造は、チームによってページが分かれています。
そのため、URLの違う部分だけ変えてアクセスすればいいので、リストに変わる部分の文字を持っておけば、あとはリストから文字列を取り出してURLを入れ替えればアクセスできます。

   #チームリスト
   team_list=['l','h','e','f','b','g','db','t','c','d','s','m']

格納用のデータフレームを用意します。
初期化のときにカラム名をしておきます。
あとから指定することもできますが、この段階でカラム名を指定しておいたほうが楽です。

   #データフレーム初期化 
   df_Pitcher = pd.DataFrame(columns=['チーム','利き投', '投手', '登板', '勝利', '敗北', 'セ|ブ', '完投', '完封勝', '無四球', '勝率',
      '打者', '投球回', '-', '安打', '本塁打', '四球', '故意四', '死球', '三振', '暴投',
      'ボ|ク', '失点', '自責点', '防御率'])
   df_Batter = pd.DataFrame(columns=['チーム','利き打', '選手', '試合', '打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
      '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球', '故意四', '死球', '三振',
      '併殺打', '打率', '長打率', '出塁率'])

URL構造は以下になります。
str(i)部分がチームの文字が入ってきます。

'http://npb.jp/bis/2020/stats/idp1op_'+str(i)+'.html'

ここは、ただURLを表示させてるだけですのでなくてもよい処理ですが、今どこのURLにアクセスして処理しているかわかるように表示させます。

print('now scraping:'+url_p)

先ほどのスクレイピングを呼び出して、データを取ります。

data_p = scrapingPitcherOpen(url_p)

スクレイピングしたあとに取ったURLのチームがわかるようにチーム列にチームのイニシャルを代入しておきます。
こうしておけば、チームでデータを抜き出したいときに楽になります。

data_h['チーム']=str(i)

スクレイピングする際は、データを取るサイトに負荷をかけないように一定の時間間隔をあけます。
ここでは1秒処理を止めるコードを入れています。

time.sleep(1)

取り出したデータを格納用のデータにつなげていく処理です。
12球団分繰り返して、データをつなげていきます。
最終的に12回ループが終わると12球団の文のデータが一つのデータフレームに格納されている形になります。

df_Pitcher=pd.concat([df_Pitcher,data_p])
df_Batter=pd.concat([df_Batter,data_h])

index値が重複していたりして番号があっていないので、振りなおします。
なくても、indexの値に重複があるだけですので、必要なければなくても良いです。

   df_Pitcher.reset_index(inplace=True,drop=True)
   df_Batter.reset_index(inplace=True,drop=True)

最後にcsvファイルに落として終わりになります。


   df_Pitcher.to_csv('Open_Pitch.csv',index=False)
   df_Batter.to_csv('Open_Hitter.csv',index=False)

今回は2020年のオープン戦成績のみ取りましたが、URLの年数部分を入れ替えれば、各年度のオープン戦データが取れます。
今回のURLで言うと下記の太字部分の2020です。

'http://npb.jp/bis/2020/stats/idb1op_'+str(i)+'.html'

自分の欲しいデータが取れるようにカスタマイズをしてみてください。

データとコードはここにあります!(有料)
スクレイピングコードを使ってみたい人、データが欲しい人はこちらへ!

ここから先は

99字 / 3ファイル
この記事のみ ¥ 300

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