※この記事は2022年3月31日に更新しています。
皆さん、こんにちは!
今回はPythonでスクレイピングをしていきます。
野球のデータの中でも最も一般的な選手の年次データをスクレイピングし、オリジナルのデータベースを作成したいと思います。
私にはデータベース周りの知識が乏しいので、今回はExcelデータに落とします。
Pythonも初心者ですので、記事執筆を通じて野球アナリストへ近づいていけたらと思います。

Contents
全体の流れ
非効率な点や至らない点も多数ありますが、今の実力的にはこれが限界です。
とりあえずアウトプットが大切だろうということで、全体の流れを示します。
- Webサイトへアクセス
- urlを取得し、2007年から2022年までループで回す
- それぞれの年度でCSVファイルを作成
- 全てのCSVファイルを合成
- チームごとのCSVファイルを出力
データが置かれているURLを取得する(io.html.read_html)
まずはデータを取得します。
今回、利用させていただいたサイトは、プロ野球データFreakさんです。
取得対照したURLは、いい加減な利用を防ぐためコード内では削除しています。
このコードを使う人は、URLを該当箇所に入れて利用してください。
サイトURLを取得した後は、そこから表データを取得します。
io.html.read_html(“URL”) を使うことで、URL内にある表形式のデータを取得できます。
また、io.html.read_html(“URL”)[表番号] でn番目の表を取得できますので、表が沢山あるサイトなどはここをいじって調整します。
io.html.read_html を使う場合、ssl証明書の有効期限切れやホスト名誤りで上記のようなエラーとなる場合があります。
エラーの内容は、Python側で、「アクセスしようとしている相手が信頼できない可能性がある」ことを示しており、警告の意味を込めたエラーとなっています。
今回は、ひとまず練習ということで、この警告を無視してコードを実行します。
処理を進めるには、以下のように 「ssl.create_default_https_context = ssl.create_unverified_context」を追加する必要があるので、コメントアウトを外して使ってみてください。
# -------------------------------------投手のデータを一括で取得します------------------------
import numpy
import pandas as pd
#import ssl
#ssl._create_default_https_context = ssl._create_unverified_context
#import matplotlib.pylab as plt
#urlをリスト形式で取得
df_all = []
#各要素に各年のデータが入る(執筆時点では2020年が最新)
# 2022年から2008年まで
years = range(22,8,-1)
urls = []
#URLを入力:最新年度だけ命名規則が違う
for year in years:
if(year==20):
urls.append('####サイトのULRを入力してください。######')
else:
# http://baseball-data.com/'+ "{0:02d}".format(year)+'/stats/pitcher-all/era-1.html
urls.append('#####')
year=20
#--------------------------データをURLから取得-------------------------------------------
for url in urls:
print('取得URL:'+url)
df = pd.io.html.read_html(url)
df = df[0]
df_all.append(df)
year=year - 1
file_path="new_" + str(year)+"_npb_pichar.csv"
df.to_csv(file_path,encoding="cp932",index=None)

取得したデータを年次ごとにまとめてCSVファイルに出力(Excelも可)
データを取得したら、CSVファイルに出力しましょう。
その前に、取得した表を見ると「順位」など不要なデータが入っていることがわかります。
それらをまずは削除し、その後各年度ごとに出力します。
df.to_csv(“ファイルパス”) を用いてます。
df_all = []
year = 21
### 年度の列を追加 ###
for year in years:
file_path= "new_" + str(year)+"_npb_pichar.csv"
df=pd.read_csv(file_path,encoding = "utf-8")
df = df.drop([0])
# year には 20 から 8までのデータが入っているので、一けたになるのを防止する
y = str(year)
if len(y) == 1:
y = "0" + y # "8" → "08" にする
df["year"] = str(20) + y
df_all.append(df)
print('読み込んだファイル:'+file_path)
df.to_csv(file_path,encoding = "utf-8",index=None)

せっかくなので、チーム→選手名→年度でソートを掛けておきます。
これで大雑把ではありますが、全データを取得できたことを確認できます。
df_result = pd.DataFrame(df_result)
del df_result["順位"]
df_result = df_result.sort_values(["チーム","選手名","year"])
df_result

各年度のCSVファイルを1つに統合する
次にこれまで集計した各年度のデータを1つのファイルにまとめていきます。
ある特定のフォルダにあるファイルを一括で指定したい時には、「glob」コマンドが役立ちます。
glob はワイルドカード(*←みたいなやつ)の基本的なパターンのことで、サンプルコード内の、「*.csv」でカレントディレクトリ内にある、CSVファイルをリスト形式で取得してくれます。
Pythonでglobを使う場合は、import する必要があるのでインポートしましょう。
後、こちらは本編と直接関係ないですが、Print()を拡張できる、pprint もインポートしておきます。
pprintを使うと、リスト形式のものを改行した形で出力できるので便利です。
コードの流れとしては、
- globでフォルダ内にある.csvファイルパスを一括で取得
- for 文で、取得したファイルパスを使って、各ファイルの中身をDataFrame形式で取得
- DataFrameをリストに保存(2と3をファイルの数だけ繰り返し)
- 複数のDataFrameを1つのDataFrameにconcat する
- concat されたDataFrameをCSVファイルとして書き出す
import glob
import pprint #リストの中身を改行したい
#csvファイルの中身を追加していくリストを用意
data_list = []
csv_files = 0
# infoファイル内のファイルを結合
csv_files = glob.glob('*.csv')
#読み込むファイルのリストを走査
print("統合するファイル一覧")
pprint.pprint(csv_files[:])
#csvファイルの中身を追加していくリストを用意
data_list = []
#読み込むファイルのリストを走査
for file in csv_files:
data_list.append(pd.read_csv(file))
#リストを全て行方向に結合
#axis=0:行方向に結合, sort
df = pd.concat(data_list, axis=0)
df.to_csv("stats_summary.csv",encoding="utf-8")

※デバックの関係で野手の成績になってしまっています。
Google Spread Sheet で開くとこんな感じに各選手のシーズン成績を一覧で表示することが出来ています!
チームごとにCSVファイルを出力
各年度ごとにファイルはできましたが、現状のファイルでは同じ年の中でしか比較ができません。
そこで、2007年から2019年までのデータを1つのファイルにまとめた後、チームごとに出力してみることにします。
そうすれば、年度をまたいだ比較が可能になります。
# チームごとのCSVファイルを作成する
for team , sdf in df.groupby('チーム'):
file_path = team + "_pichar.csv"
print(i)
print(sdf)
sdf.to_csv(file_path,encoding="cp932",index = False,columns = ind_list)
実行したファイルの中に、チームごとのCSVファイルが出力されます。
現状では、これ移行の分析はExcelでどうぞ…となってしまいます。
おまけ NPBの選手名簿を作る
成績にはあまり関係ありませんが、選手の年齢や年俸などのデータを取ってみたいと思います。
単純な成績の比較に加えて、チーム内の年齢構成などが分かればより深い分析になるはずです。
# 選手名を重複なく抽出し、CSVファイルを作成
df_all = []
name_list = []
dic = {}
for year in range(19,8, -1):
file_path="new_" + str(year)+"_npb_personaldata.csv"
df = pd.read_csv(file_path,encoding="cp932")
df = df.drop([0])
for i in range(1, len(df)):
player_name = df['選手名'][i]
name_list.append(player_name)
# 重複を削除する
name_list = set(name_list)
file_path="npb_personaldata_name.csv"
name_list = pd.DataFrame(name_list,columns=["選手名"])
name_list.to_csv(file_path,encoding="cp932",index = None)
df_name = pd.read_csv("npb_personaldata_name.csv", encoding = "cp932")
df_name
df_result = []
# 選手名のリストをインポート
#print(df_name["選手名"][i])
# 年度ごとのファイルをインポート
for year in range(19,8, -1):
file_path="new_" + str(year)+"_npb_personaldata.csv"
df = pd.read_csv(file_path,encoding="cp932")
for i in range(1, len(df_name)):
player_name = df_name['選手名'][i]
for j in range(1, len(df)):
if player_name == df['選手名'][j]:
# print(df[player_name])
df_result.append(df.loc[j])
break
else:
continue
print(str(year) + player_name + "のデータ抽出が終わりました")

まとめ
Pythonを使って、野球データのスクレイピングを行いました。
年次データ程度では、真新しい分析はできませんので、今後はもう少し細かいデータの取得や分析を目指していこうと思います。
最後に、この記事でやったことのおさらいです。
- Webサイトへアクセス
- urlを取得し、2007年から2020年までループで回す
- それぞれの年度でCSVファイルを作成
- 全てのCSVファイルを合成
- チームごとのCSVファイルを出力
今回使ったコードのURLや表番号を変えることで様々なサイトの情報を取得できると思います!

サンプルデータ
最後に、上記のコードを実装後に手に入るデータをサンプルとして置いておきます。(ただし野手版です)
サンプルデータとして、分析などに使ってみてください!
スクレイピングって何…?という人でも簡単にトレースできると思います!