第2話では、アーキテクトを軸に書かせていただきました。当面はこのアーキテクトをベースに作り込んで予測して、週末にはドキドキして、また改良してを繰り返していきたいと思います。
そんな本記事では、データの取得と前処理について書いていきたいと思います。ぜひ最後までご覧ください。
Jリーグの結果を取得する
この記事を参考にしてください。
現時点で私が活用しているコードは以下の通りです。
import csv
from urllib.request import urlopen
from bs4 import BeautifulSoup
import ssl
import os
import numpy as np
import time
from tqdm import tqdm
import settings
ssl._create_default_https_context = ssl._create_unverified_context
# URLの指定
year_list = ["2023","2022", "2021", "2020", "2019", "2018", "2017", "2016", "2015",
"2014", "2013", "2012", "2011", "2010", "2009", "2008", "2007",
"2006", "2005", "2004", "2003", "2002", "2001", "2000", "1999",
"1998","1997","1996","1995","1994","1993",]
dir = './source'
if not os.path.exists(dir):
os.makedirs(dir)
for year in tqdm(year_list, total=len(year_list)):
time.sleep(5)
html = urlopen(f"https://data.j-league.or.jp/SFMS01/search?competition_years={year}&tv_relay_station_name=")
bsObj = BeautifulSoup(html, "html.parser")
# テーブルを指定
table = bsObj.findAll("table", {"class":"table-base00 search-table"})[0]
rows = table.findAll("tr")
with open(f"./source/{year}_results.csv", "w", encoding='utf-8') as file:
writer = csv.writer(file)
for row in rows:
csvRow = []
for cell in row.findAll(['td', 'th']):
csvRow.append(cell.get_text())
writer.writerow(csvRow)
いくつかの参考サイトから各要素を拝借して作りました。実際にデータを取得してきているサイトは以下になります。データを見つつイメージを膨らませていただければと思います。このページの表をそのまま取得してくるイメージになります。
上記を実行することで、カレントディレクトリにsourceというフォルダが生成され、その中に年別のcsvファイルが生成されます。そこまで大きなデータにはなりませんが、そのまま実行せずに少しご注意ください。
前処理
この前処理の工程は結構大変な作業になります。スクレイピングでデータを取得してくると、かなりごちゃごちゃした汚いデータが出てきてしまうので、これを前処理できれいにしていきます。
データを1テーブルにして保存する
まずは年別となっているデータをまとめて1つのテーブルにします。適宜ちゃんとうまく処理がされているかを確認するために、冗長ではありますがcsvに保存していきますので、ご注意ください。
import glob
import pandas as pd
data_paths = glob.glob("./source/*_results.csv")
df = pd.DataFrame()
for path in data_paths:
csv_data = pd.read_csv(path)
df = pd.concat([df, csv_data])
dir = './steps'
if not os.path.exists(dir):
os.makedirs(dir)
df['match_no'] = range(1, len(df.index) + 1)
df = df.astype({'match_no':str})
df.to_csv("./steps/step_1.csv", index=False)
stepsというディレクトリを生成して、途中経過の生成データを保存していきます。
この工程では、年別のデータをフォルダから読み込んで結合しているだけのシンプルな処理になります。今のところ使いこなせていませんが、この段階で一意のmatch_idを付与しています。
不要な文字列を除去
# データフレームの整形
df_mod = df.copy()
strip_columns = ['年度', '大会', '節', '試合日', 'K/O時刻', 'ホーム', 'スコア', 'アウェイ', 'スタジアム', '入場者数','インターネット中継・TV放送','match_no']
for columns in strip_columns:
if columns == "年度":
continue
df_mod[columns] = df_mod[columns].str.strip()
df_output = df_mod[strip_columns]
df_output.to_csv("./steps/step_2.csv", index=False)
こうやるとうまく処理できます。冗長な感じになっていますが、悪しからず。
スコア列の修正と勝敗記号の生成
この処理では、文字列で「2-1」などとなっていたり、PKの結果が入っていたりなどなっているスコアのデータを修正します。
まずPKの結果は除去します。次にスコアをホームのスコアとアウェイのスコアで1列ずつ生成して、扱いやすいようにします。またスコアの情報から、勝敗記号「1」「2」「0」を生成します。
# スコアの修正をし、スコア列を作っていく
import re
base_df = df_output.copy()
base_df["スコア"] = base_df["スコア"].replace(re.compile(r"\(PK\d+-\d+\)"), '', regex=True)
base_df_score = pd.concat([base_df, base_df["スコア"].str.split('-', expand=True)], axis=1)
base_df_score_mod = base_df_score.rename(columns={0: 'home_score', 1: "away_score"})
base_df_score_mod["home_score"] = base_df_score_mod["home_score"].replace(re.compile(r"vs"), np.NaN, regex=True)
base_df_score_mod
# スコアから勝敗記号を生成する
import numpy as np
base_df_score_mod['result'] = np.where(base_df_score_mod['home_score']>base_df_score_mod['away_score'], "1", np.where(base_df_score_mod['home_score']<base_df_score_mod['away_score'], "2", "0"))
# この段階を一旦保存
base_df_score_mod.to_csv('./steps/step_3.csv', index=False)
こんな感じを実行すると、step3.csvが生成されます。
まとめ
本記事はここまでにします。次回も引き続き前処理工程を紹介してまいります。
スクレイピングによるJリーグの試合結果の取得と、そのデータの前処理入り口を紹介しました。Jリーグのデータを取得できれば、スキルのある方であれば如何様にでも調理できると思います。そんな方々の一助となれれば嬉しいですし、単純に当選するまでの物語として楽しんでいただければさらに嬉しいです。
それでは次回の記事をお楽しみに。
コメント