프로젝트 동기가 KBL의 '2Q 종료 후 기대 승률' 이었고, 저의 주 관심사 역시 KBL이기 때문에 KBL의 데이터로 프로젝트를 진행하고 싶었습니다. 하지만 현재 KBL 에서는 쿼터별 데이터로는 득점 데이터만 제공하고 있습니다. 리바운드 갯수, 야투율과 같은 경기 세부 스탯의 경우 경기 전체(4쿼터 전체) 데이터만 제공하고 쿼터별(혹은 전/후반 별) 데이터는 제공하지 않습니다.(홈페이지의 경기 기록 메뉴에서 해당 데이터가 제공되지 않아 KBL측에 따로 문의하였으나 홈페이지에서 제공하는 데이터 외에는 제공이 어렵다는 답변을 받았습니다.ㅠㅠ) 하지만 본 프로젝트에서는 앞서 언급한 것 처럼 기대 승률 및 승부 예측에 전반전의 세부 스탯들을 적용하고자 하기 때문에 최소한 경기의 전/후반으로 구분된 팀별 세부 스탯 데이터가 필요했습니다.
이에 동일한 내용의 프로젝트를 KBL이 아닌 NBA 데이터를 활용하여 진행하기로 했습니다. NBA 경기의 경우 Basketball-Reference 라는 NBA 관련 데이터를 제공하는 사이트가 존재하고, 해당 사이트에서 아래 사진과 같이 각 경기에 대한 팀별 전/후반 혹은 쿼터별 세부 스탯을 확인할 수 있기 때문이었죠.
하지만 해당 basketball-reference 사이트 역시 data를 보여주기만 할 뿐, 유저가 원하는 형태의 데이터를 뽑아 다운받을 수 있는 시스템은 아닙니다. 이에 BeautifulSoup 패키지를 사용하여 web scraping을 통해 각 경기의 팀별 전반 세부 스탯 데이터를 뽑아 필요한 데이터셋을 생성하는 코드를 작성했습니다. 코드는 다음과 같습니다.
# needed libraries
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import numpy as np
STAT=pd.DataFrame(columns=['Team_H','Team_A','HomeWin',
'MP_H','FG_H','FGA_H','FG%_H','3P_H','3PA_H','3P%_H','FT_H','FTA_H','FT%_H','ORB_H','DRB_H','TRB_H','AST_H','STL_H','BLK_H','TOV_H','PF_H','PTS_H',
'MP_A','FG_A','FGA_A','FG%_A','3P_A','3PA_A','3P%_A','FT_A','FTA_A','FT%_A','ORB_A','DRB_A','TRB_A','AST_A','STL_A','BLK_A','TOV_A','PF_A','PTS_A'])
basic_url="https://www.basketball-reference.com"
date_url="https://www.basketball-reference.com/boxscores/?month=5&day=16&year=2021"
while (STAT.shape[0]<=1230):
urls=[]
response = requests.get(date_url)
soup=BeautifulSoup(response.text,"html.parser")
for suburl in soup.find_all("td",{"class":"right gamelink"}):
urls.append(basic_url+suburl.find("a").get('href'))
for j in range(len(urls)):
response_temp=requests.get(urls[j])
print(urls[j])
soup_temp=BeautifulSoup(response_temp.text, 'html.parser')
soup_strong=soup_temp.find_all("strong")
for k in range(len(soup_strong)):
if(len(soup_strong[k].get_text())==3):
home_team=soup_strong[k].get_text()
away_team=soup_strong[k+1].get_text()
break;
score_list=soup_temp.find_all("div",{"class":"score"})
score=[]
for eachscore in score_list:
score.append(eachscore.get_text())
if score[0]>score[1]:
homewin="1"
else:
homewin="0"
class1=soup_temp.select('div#div_box-'+home_team+'-h1-basic table#box-'+home_team+'-h1-basic tfoot td')
class2=soup_temp.select('div#div_box-'+away_team+'-h1-basic table#box-'+away_team+'-h1-basic tfoot td')
df=[]
df.append(home_team)
df.append(away_team)
df.append(homewin)
for i in range(len(class1)-1):
df.append(class1[i].get_text())
for i in range(len(class2)-1):
df.append(class2[i].get_text())
STAT.loc[STAT.shape[0]]=df
num=np.random.rand(1)[0]*5
time.sleep(num)
print(STAT.shape[0])
prev_url=soup.find("div",{"class":"prevnext"}).find("a").get('href')
date_url=basic_url+prev_url
STAT.head(10)
STAT.tail(10)
scraping 코드 작성에 참고한 자료 : https://youtu.be/TbPD9Ndnt04
코드의 date_url에 아래 사진과 같은 특정 날짜의 경기 결과가 모여있는 페이지의 url을 입력하고 코드를 실행시키면 해당 날짜로부터 하루씩 이전으로 가면서 각 날짜에 실시된 경기 별로 홈팀/원정팀의 전반 세부 스탯과 경기 결과를 scraping하여 STAT 데이터프레임에 추가합니다.
전체 scraping은 시즌 단위로 진행됩니다. 각 시즌의 마지막 경기 날짜에 대한 페이지 URL을 date_url에 입력하고 while문의 조건을
(STAT.shape[0]<=해당시즌의총경기수)
로 설정하면 해당 시즌의 모든 경기에 대해 경기별 전반전 세부 스탯 데이터를 scragping 하여 하나의 데이터프레임으로 만들 수 있습니다. 기존 NBA는 30개 팀이 82경기씩 치러 총 1,230 경기가 치러지지만 2022-2023 시즌은 현재 진행중이기 때문에 경기 수가 1230 보다 적고, 2020-2021시즌은 COVID19의 여파로 팀별로 10경기씩 적게 진행되어 총 1080 경기가 진행되었습니다.(72x30/2=1080) . 이렇게 생성한 2020-2021, 2021-2022, 2022-2023 시즌의 데이터를 csv 파일로 저장한 뒤 하나의 파일로 합쳐 프로젝트에 사용하였습니다. 단, 플레이오프 경기는 특성상 예외적인 사항이 많이 발생하기 때문에 정규리그 경기의 데이터만 사용하였습니다.