No.021 MLBデータ分析

1.今回の目的

Beautiful SoupのようなPythonのWebスクレイピングライブラリを使えば、Webサイトからのデータ収集を自動化することができます。
この記事では、Beautiful Soupを使って2022年シーズンのMLB選手のスタッツを収集します。

具体的には、次のような方法を取り上げます。

ビューティフルスープとリクエストのセットアップ
Requestsを使用してESPNウェブサイトに接続
Beautiful Soup を使って MLB の選手の統計情報を抽出
Pandas DataFrame にスクレイピングされた選手の統計情報を入力

2.Reference

こちらのサイトを参考にしました。

3.Pythonでの実行

Webスクレイピングを行うためのPython環境を整えるために、RequestsとBeautiful Soupという2つのPythonパッケージをインストールする必要があります。Requestsはウェブサイトのソースコードを読むためのライブラリで、Beautiful SoupはこのHTMLソースコードをタグに基づいて検索・解析するためのライブラリです。この2つのパッケージは、Anaconda Powershell等のコマンドラインインターフェイスで以下のコマンドを実行するだけでインストールできます。pip install requests beautifulsoup4  次に、2つのimport文を使用して、選択したPython開発環境(Jupyter Notebookを使用)で使用するためにこれらのライブラリをインポートします。

import requests
from bs4 import BeautifulSoup

ついでに、プログラムを書くときに便利なPandasとRegular Expressions (re)もインストールします。

import pandas as pd
import re
import requests
from bs4 import BeautifulSoup

requests.get()  ウェブサイトに接続
.get()メソッド ウェブサイトの背後にあるソースコードを見る
Beautiful Soupがソースコードであるpage.textをHTMLとしてパースするように、引数html.parserを含めて変数soupに保存するように指定します。

url ='https://www.espn.com/mlb/history/leaders/_/breakdown/season/year/2022/start/301'
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')

次に、Beautiful Soupの.find_all()とfind()メソッドを使って、ソースコードから情報を抽出します。Beautiful Soup().find_all() は、HR(ホームラン)やRBI(打点)などの選手の統計情報を含む要素を探し出し(find_all()は指定したタグや属性のHTMLソースコードを検索)ます。まず、find_all()は、スクレイピングしたい要素のタグを指定することを要求します。次に、.find_all()のattrs引数で属性を指定しますが、これは以下のような構成になっています。

attrs = {'attribute1_name': 'attribute1_value', 'attribute2_name': 'attribute2_value'}

.find_all('tr', attrs={'class':'colhead'}) はテーブルに現れる5つのテーブルヘッダのリストを返します。9/16時点打率トップ(リストの1番上)Freddie Freemanの打撃成績の要素が取り出せました。

soup.find_all('tr', attrs={'class':'colhead'})

Freddie Freemanの行の最後の<td>要素打率 (BA)=329になります。 ソース コードでは、<td> タグは <td class="sortcell">.329</td> であることがわかります。

Beautiful Soupの要素で実行して呼び出す.get_text()メソッドでテキストだけを選択することができます。このメソッドは、タグ表記(<>)の外側にあるテキストをすべて抽出します。

soup.find_all('tr', attrs={'class':'colhead'})
row = soup.find('tr', attrs = {'class': 'oddrow player-10-30193'})
for data in row.find_all('td'):
    print(data.get_text())

Dataを取り出せることがわかったの、今度はDataのヘッダーを取り出します。Headerは1つなのでsoup.findで識別して For分で1つずつColumnsに抽出していき、pd.DataFrameで、列名のみの空のDataframeを作成します。

#テーブルのヘッダーとのコラム列の作成
##ヘッダー行の識別
header = soup.find('tr', attrs={'class':'colhead'})
##ヘッダー行からコラム列名の抽出
columns = [col.get_text() for col in header.find_all('td')]

#空のデータフレームの作成
final_df = pd.DataFrame(columns=columns)
final_df

ESPNのWebサイトを検証でみてみると選手のDataはパターン化されていることがわかります。具体的にはFreddie Freeman<tr class="oddrow player-10-30193" align="right">となっており、2位のPaul Goldschmidtは、<tr class="evenrow player-10-31027" align="right">となっています。<tr class="oddrow player-10-番号, <tr class="evenrow player-10-番号が繰り返されています。

この場合、Regular Expressions (re)の、re.complilen’s compileを使うと、"row player-10-"を含む行を全てとってきてくれるそうです。
これを使いsoup.find_allで全行をplayersに格納します。

for文を使い、全てのデータをtemp_dfにいれて、final_dfに入れ込みます。

players = soup.find_all('tr', attrs={'class':re.compile('row player-10-')})

for player in players:
    stats = [stat.get_text() for stat in player.find_all('td')]
    temp_df = pd.DataFrame(stats).transpose()
    temp_df.columns = columns
    
    final_df = pd.concat([final_df, temp_df], ignore_index=True)

無事Dataframeに格納できました。

でもさすがにメジャーだけあって319人もリストがあってURLは5ページ分あります。ただ、urlの末尾のstart/{}のところに数字をいれると、その順位の選手がリストの一番上で確認できることがわかりました。
なのでfor文でurlの末尾に1-319までの数字をいれてデータ取得をくりかえします。これで無事に319人のリストを取ることができました!

for i in range(1,319,50):
    url = 'https://www.espn.com/mlb/history/leaders/_/breakdown/season/year/2022/start/{}'.format(i)
    page = requests.get(url)
    soup = BeautifulSoup(page.text)
    players = soup.find_all('tr', attrs={'class':re.compile('row player-10-')})
    for player in players:
        stats = [stat.get_text() for stat in player.find_all('td')]
        temp_df = pd.DataFrame(stats).transpose()
        temp_df.columns = columns
        final_df = pd.concat([final_df, temp_df], ignore_index=True)
            
final_df

以上です。これでメジャー選手の成績分析も可能になりました!

この記事が気に入ったらサポートをしてみませんか?