B.Leagueの公式データ(スタッツ)を慌てて取得
B.League公式データを慌てて取得
nbl時代にもやっていたのですがテーブル構造が雑なために挫折していて、B.League 2017-18シーズンに少しずつ取得を試みていたのですがそれも未完成だったのでようやく再開しました。
きっかけはスタッツページのデフォルト表記が2018-19シーズンになったことで、2017-18シーズンのスタッツが取りにくくなっていくことを危惧したからです。
が
https://www.bleague.jp/stats/?tab=1&year=2017
になっただけなので、実際は年号が増えただけでしたが他にも何かあるかもと焦り行動に移せました。
スクレイピング(データ収集)
webページのデータを取得してくることを スクレイピング と言います。 どんなプログラム言語でも実現可能だと思います。nbl時代はphp、2016時代はrubyで試していましたが、今回は分析と言えばpythonと言う思い込みからpythonを使うことにしました。
Rをお使いの場合は rintaromasuda さんの記事を参考にすると良いと思います。自力でデータ取れなかった場合に備えデータ共有していただき大変助かりました。
スクレイピングでBリーグ全選手の試合ごとスタッツを取得する - データで観るBリーグ
どうやったか?
Pythonを選びましたが…Python使ったこと無いのでmacの環境構築からスタート。
環境構築
# brew install python3 # brew install mysql@5.6 # pip3 install mysqlclient # pip3 install requests # pip3 install webdriver # pip3 install selenium # pip3 install beautifulsoup4
MySQLを5.6指定したくらいで後はすんなり入りました。
シーズン通しての個人スタッツ
シーズン通しての個人スタッツであればプログラム書かずにブラウザ表示してコピペで取得できます。少し手直しするだけで十分使えますがpythonで取得します。
import csv from selenium import webdriver from bs4 import BeautifulSoup def scraping(): options = webdriver.chrome.options.Options() driver = webdriver.Chrome(chrome_options=options) driver.get(url) html = driver.page_source.encode('utf-8') soup = BeautifulSoup(html, "html.parser") table = soup.find("table", {"class": "original"}) rows = table.findAll("tr") with open(output_file, "wt", newline='', encoding='utf-8') as file: for row in rows: # rows整形する処理 file.write() if __name__ == '__main__': url = "https://www.bleague.jp/stats/?tab=1&year=2017" output_file = 'stats.csv' scraping()
流れとしたは下記の通りです。
- urlを指定
- driver設定
- 文字コード指定してhtmlのソース取得
- 扱いやすいようにパース
- 指定したテーブルだけ抜き出す
- 1行
ごとに分割して処理をしやすくする - csv出力する
- csvをExcelやDBにインポート
基本的にはコレで十分なのですが、私は少し整形を加えました。具体的には...
- 選手名からリンクしている個別スタッツのための選手ID(PlayerID)抽出
- クラブ名やポジションごとにIDを振り割当(冗長)
- 時間を全て秒にも変換 (分:秒の書式は扱いにくい)
- アベレージ系は再計算して小数点以下第2位まで求める
試合ごとの個人スタッツ
試合ごとの個人スタッツは選手ごとにページがあり、そこに載っています。さすがに全ページコピペするわけにはいきません。ここで先程取得した選手ごとのID(PlayerID)を使ってシーズン全試合のスタッツを取得します。
https://www.bleague.jp/roster_detail/?tab=1&year=2017&PlayerID=8589#player-stats
↑これが三河から栃木への移籍で今話題の比江島慎のスタッツページのURLです。
↓ 話題の情報はこちら(明日記者会見) www.tochigibrex.jp
そしてプログラムはこちら
def scraping(): with open(output_file, "wt", newline='', encoding='utf-8') as file: csv_row = [] players = get_players() # player_idを取得 for tp in players: plyer_id = tp[0] table = get_table_data(player_id) rows = table.findAll("tr") # 1行ごとの処理 for row in rows: txt = '' for cell in row.findAll(['td', 'th']): txt += cell.get_text() + ',' file.write(txtを整形するメソッド) # 対象tableをスクレイピング def get_table_data(player_id): options = webdriver.chrome.options.Options() options.add_argument("--headless") driver = webdriver.Chrome(chrome_options=options) tag = '#' + table_id url = base_url + str(player_id) + tag driver.get(url) html = driver.page_source.encode('utf-8') soup = BeautifulSoup(html, "html.parser") table = soup.find("tbody", {"id": table_id}) return table if __name__ == '__main__': base_url = "https://www.bleague.jp/roster_detail/?tab=1&year=2017&PlayerID=" year = 2017 table_id = "fifthgames" scraping()
少し長くなりましたが基本的には同じことをしています。対象となるURLを生成し、抽出するテーブルのID指定が異なります。 流れとしては下記の通りです。
- ベースとなるURLを用意
- PlayerIDを1つずつ処理するループ
- PlayerIDとtableのIDを結合してURLを作る
- tbodyのidでfifthgamesを指定して取得
- 1行
ごとに分割して処理をしやすくする - csv出力する
- csvをExcelやDBにインポート
ここでも少し整形を加えました。
- スターター判定(○を1にしただけですが)
- 時間を全て秒にも変換 (分:秒の書式は扱いにくい)
- アベレージ系は再計算して小数点以下第2位まで求める
おわりに
非常に殴り書きな感じになってしまったのですが、忘れないうちにまとめておきたかったためです。。説明ももう少し丁寧に書き直せるといいのですが、、、追々やるとします。
全選手の試合ごとのデータ取得には1選手ごとに5秒くらいで全体では20分近くかかりました。もっとうまくループを回せば少しくらいは早くなりそうなのでそこも課題です。
何はともあれデータは無事取得できたのでゴニョゴニョしたいと思います。
当初の壮大な理想はクォーターごとのスタッツ取得なのですが、想像以上に大変なことが分かったので保留とします。それがあれば前半/後半やオンザコート1と2(ムリありそう)での比較なんかもできそうなんですが...
2017-18 B1リーグ 2017/09/29 栃木 VS 三河 - B.LEAGUE(Bリーグ)公式サイト
データの見方に関するアドバイスやご指摘大歓迎です。