머신러닝과 기상 데이터를 이용해 홈런 갯수 예측해보기 (2) 홈런 데이터 가져오기

    목차
반응형

https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=20200508SKLT02020&category=kbo

5월 8일 SK와 롯데의 경기 결과다.

저번 글의 코드를 이용해서 위의 주소를 자동으로 뽑아냈으니 이제 이 결과에서 홈런 수를 빼내야 한다.

 

url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=20200508SKLT02020&category=kbo"
req = requests.get(url)
html = req.text
print(html)
(생략)
DataClass = jindo.$Class({
_data : {"etcRecords":[{"result":"없음","how":"결승타"},{"result":"안치홍1호(2회1점 문승원) 한동민3호(5회1점 노경은) 전준우2호(6회1점 문승원) 최정1호(7회1점 박시영) 이대호1호(7회2점 박민호) 마차도2호(8회1점 서진용) ","how":"홈런"},{"result":"고종욱(2회) \r\n\r\n","how":"3루타"},{"result":"손아섭(1회) 김창평(2회) 정현(2회) 정훈(4회) 이대호(6회) 전준우(7회) 로맥(7회) \r\n\r\n","how":"2루타"},{"result":"고종욱(1회) 정진기(4회) 김창평2(4 6회) 안치홍(10회) \r\n\r\n","how":"도루"},{"result":"안치홍(6회) \r\n\r\n","how":"도루자"},{"result":"김준태(7회) \r\n\r\n","how":"포일"},{"result":"문승원(2회) 김주한(10회) \r\n\r\n","how":"폭투"},{"result":"이민호 강광회 이용혁 김정국","how":"심판"}]
(생략)

중간에 잘 찾아보면 이런 식으로 코드가 적혀있다.

2020년 5월 8일 SK:롯데 경기결과 (네이버 스포츠)

위 스크린샷과 비교했을 때 같은 정보임을 확인할 수 있다.

 

이제 본격적으로 홈런 갯수를 뽑아내보자.

# 홈런 상세내용
def cuturl2(url):
    req = requests.get(url)
    html = req.text
    
    pos0 = html.find('"how":"결승타"')
    pos1 = html.find('"how":"홈런"')

    if pos1 == -1 :
        return ''   
    
    return html[pos0+13:pos1+11].strip()

# 홈런 갯수
def findhomeruns(st, i):
    inning = st.find("회")
    if inning == -1 :
        return i
    try :
        int(st[inning-1])
    except :
        return findhomeruns(st[inning+1:], i)

    return findhomeruns(st[inning+4:], i+1)

결승타~홈런 사이를 잘라주고 홈런이 없으면 빈 string을 반환하도록 했다.

그 후 잘라준 string에서 '회'라는 단어가 몇 번 나오는지 체크했다. 모든 홈런 정보에 x회y점이라고 되어 있기 때문이다.

다만 김승회 선수의 이름처럼 이름에 '회'가 들어가는 경우도 있기 때문에 회라는 단어 앞에 숫자가 오면 홈런 갯수인 것이고 아니면 다시 찾도록 했다.

 

 

이제 찾을 건 다 찾았으니 정리를 해주면 끝이다.

# 총정리
def details(yyyymm, score):
    tmp = []
    (year, month) = ym(yyyymm)

    teams = {"두산" : "OB", "키움" : "WO", "넥센" : "WO", "SK" : "SK", "LG" : "LG", \
             "NC" : "NC", "KT" : "KT", "kt" : "KT", "KIA" : "HT", "삼성" : "SS", \
             "한화" : "HH", "롯데" : "LT"}
    for (month, date, away, home, double, stadium) in score :
        month = str(month)
        date = str(date)
        if len(month) == 1 :
            month = '0'+month
        if len(date) == 1 :
            date = '0'+date
        url = geturl2(year, month, date, teams[away], teams[home], double)
        st = cuturl2(url)
        if len(st) == 0 :
            n = 0
        else :
            n = findhomeruns(st, 0)
        tmp.append((year, month, date, n, stadium))
    return tmp

# pandas에 저장
def regist(x, df):
    for (year, month, date, n, stadium) in x:
        l = len(df["YEAR"])
        df.loc[l] = [int(year), int(month), int(date), n, stadium]



base_dir = 'C:/Users/Username/Desktop/'
excel_file = "homeruns.xlsx"
excel_dir = os.path.join(base_dir, excel_file)
df = pd.read_excel(excel_dir, encoding="EUC_KR")


year_list = [(200803, 200811), (200904, 200911), (201003, 201011), (201104, 201111), (201204, 201212), \
            (201303, 201312), (201403, 201412), (201503, 201511), (201603, 201612), (201703, 201711), \
            (201803, 201812), (201903, 201911)]

for (date_start, date_end) in year_list:
    for date in range(date_start, date_end):
        url = geturl(date, "")
        st = cuturl(url)
        ls = cutst(st)
        sc = score(ls)
        x = details(date, sc)
        print(str(date)+" OK")
        regist(x, df)


df.to_excel(excel_file, index = False)
print("Saved!")

엑셀을 열고 첫 행에 YEAR, MONTH, DATE, HRS, STADIUM을 적은 뒤 homeruns.xlsx 파일을 미리 만든 뒤 파이썬을 실행했다.

얼마간의 시간이 지나면 잘 저장되어 나오는 걸 확인할 수 있다.

homeruns.xlsx
0.17MB

 

여기서 두 가지 문제점이 생겼다.

 

1. 2008년부터 2010년까지 (불확실) 가을야구 데이터가 누락이 된 게 많았다.

몇몇은 아예 결과가 없었고 몇몇은 아직도 중계중이라는 엄청난 상태인 채였다. (중계중이면 경기결과 주소가 아예 생성이 안 된다.)

그래서 KBO 홈페이지와 스탯티즈에 들어가서 홈런 수를 확인하고자 했지만 양쪽에서 정보를 찾을 수 없었다.

결국 뉴스 검색을 통해 홈런 갯수를 수작업으로 추가해주었다.

 

2. 언젠가부터 시범경기 결과도 경기결과 리스트에 있어서 그것도 수작업으로 제외해줬다.

차라리 가을야구처럼 주소가 다르면 편할텐데 정규시즌과 같은 주소 형태를 쓰고 있어서 아쉬웠다.

 

 

이런 작업을 거친 뒤 homeruns.xlsx라는 파일이 완성되었다. (2008년~2019년 정규시즌+포스트시즌 홈런 갯수)

 

 

 

(다음 글에 이어서)

 

 

전체 코드

더보기
#homeruns.py

import urllib.request
from bs4 import BeautifulSoup
import pandas as pd
import os
from numpy import NaN

import requests

# url 가져오기
def geturl(date, teamname):
    (year, month) = ym(date)
    url = "https://sports.news.naver.com/kbaseball/schedule/index.nhn?&month="+str(month)+"&year="+str(year)+"&teamCode="+teamname
    return url


# 날짜 자르기
def ym(date):
    year = str(date)[:4]
    month = str(date)[-2:]
    return (year, month)

# 앞뒤 간편하게 자르기
def cuturl(url):
    req = urllib.request.Request(url)
    html = urllib.request.urlopen(req).read()
    soup = BeautifulSoup(html, "lxml")
    st = soup.get_text()

    cut1 = st.find("알림받기")
    cut2 = st.find("야구 홈으로")
    st = st[cut1+4:cut2]
    st = st.strip()

    return st

# 각 날짜별로 나누기
def cutst(st):
    teamlist = ["SK ", "SK\n", "삼성", "LG ","LG\n", "롯데", "KIA", "한화", "넥센", "키움", "KT", "NC", "두산", "kt"]
    tmp = []

    while ("SK "in st) or ("SK\n" in st) or ("삼성" in st) or ("LG " in st) or ("롯데" in st) or ("KIA" in st) \
            or ("한화" in st) or ("넥센" in st) or ("키움" in st) or ("KT" in st) or ("kt" in st) \
            or ("NC" in st) or ("두산" in st) or ("LG\n" in st) :
        tmp2 = {}
        tmp3 = []
        for team in teamlist:
            pos = st.find(team)
            if pos == -1 :
                continue
            else :
                tmp2[team] = pos

        for k, v in tmp2.items():
            tmp3.append((v, k))
            tmp3 = sorted(tmp3)
        if len(tmp3) == 0 :
            break

        pos1 = tmp3[0][0]
        pos2 = tmp3[1][0]
        
        st1 = st[:pos1].strip()
        st2 = st[pos1:pos2+4].strip()
        
        st1 = finddate(st1)

        st3 = st[pos2+4:]

        co = st3.find(":")
        dot = st3.find(".")

        if (co < 0) and (dot>0) :
            pos3 = dot
            st3 = st3[:pos3-5].strip()
            st3 = st3[-4:].strip()
        elif (co < 0) and (dot<0) :
            st3 = st3[-4:].strip()
        elif (co > 0 ) and (dot < 0) :
            pos3 = co
            st3 = st3[:pos3-5].strip()
            st3 = st3[-4:].strip()
        else :
            pos3 = min(co, dot)
            st3 = st3[:pos3-5].strip()
            st3 = st3[-4:].strip()

        
        if "(LG)" in st3:
            st3 = st3[:2]

        pos4 = st2.find("SKY")
        if pos4 > 0 :
            st2 = st2[pos4+10:].strip()
        
        tmp.append((st1, st2, st3))
        st = st[pos2+3:].strip()

        sky = st.find("SKY")

        if (sky > 0 ) and (sky < 30):
            st = st[sky+10:].strip()
            
    return tmp

# string에서 날짜 찾기
def finddate(st):
    pos1 = st.find("없습니다")
    pos2 = st.find("V2")
    pos3 = st.find("2TV")
    if (pos1 >= 0) or (pos2 >= 0) or (pos3 >= 0):
        pos = max(pos1, pos2, pos3)
        st = st[pos+5:].strip()
        return finddate(st)
    try :
        if len(st) == 0 :
            return st
        x = int(st[0])
        return st
    except :
        st = st[1:]
        return finddate(st)

# 날짜/홈팀/상대팀으로 정리하기
def score(ls):
    tmp = []
    i = -1
    dtmp = []
    
    for datestring, teamscore, stadium in ls :
        pos0 = datestring.find(".")
        pos1 = datestring.find("(")
        pos2 = datestring.find(")")

        month = datestring[:pos0].strip()
        date = datestring[pos0+1:pos1].strip()

        try :
            month = int(month)
            date = int(date)
            dtmp.append((month, date))
            i = i + 1
            sameday = len(tmp)
        except :
            month = dtmp[i][0]
            date = dtmp[i][1]


        if "VS" in teamscore:
            continue
        else :
            away = teamscore[:3].strip()
            home = teamscore[-3:].strip()

        # 더블헤더 체크
        double = 0
        if len(tmp) > sameday :
            for (month2, date2, away2, home2, double2, stadium2) in tmp[sameday:]:
                if away2 == away:
                    double = 1
                    break
            
        tmp.append((month, date, away, home, double, stadium))
    return tmp

# 경기 상세 기록 url 가져오기
def geturl2(year, month, date, away, home, double) :
    
    if int(year) <= 2015 :
        url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                +str(year)+str(month)+str(date)+away+home+"0"+"&category=kbo"
        req = requests.get(url)
        html = req.text

        if "잘못된 접근" in html :
            if double == 0 :
                url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                        +str(year)+str(month)+str(date)+away+home+"1"+"&category=kbo"
            else :
                url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                        +str(year)+str(month)+str(date)+away+home+"2"+"&category=kbo"

    else :
        url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                +str(year)+str(month)+str(date)+away+home+"0"+str(year)+"&category=kbo"

        req = requests.get(url)
        html = req.text
        
        if "잘못된 접근" in html :
            if double == 0 :
                url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                        +str(year)+str(month)+str(date)+away+home+"1"+str(year)+"&category=kbo"
            else :
                url = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                        +str(year)+str(month)+str(date)+away+home+"2"+str(year)+"&category=kbo"


    #가을야구
    req = requests.get(url)
    html =req.text
    postseason = [3333, 4444, 5555, 7777]
    for i in postseason :
        if int(year) <=2015 :
            url2 =  "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                    +str(i)+str(month)+str(date)+away+home+"0"+"&category=kbo"
        else :
            url2 = "https://sports.news.naver.com/gameCenter/gameRecord.nhn?gameId=" \
                    +str(i)+str(month)+str(date)+away+home+"0"+str(year)+"&category=kbo"
            
        req = requests.get(url2)
        html = req.text
        if "잘못된 접근" not in html :
            url = url2
            break
        
    return url

# 홈런 상세내용
def cuturl2(url):
    req = requests.get(url)
    html = req.text
    
    pos0 = html.find('"how":"결승타"')
    pos1 = html.find('"how":"홈런"')

    if pos1 == -1 :
        return ''   
    
    return html[pos0+13:pos1+11].strip()

# 홈런 갯수
def findhomeruns(st, i):
    inning = st.find("회")
    if inning == -1 :
        return i
    try :
        int(st[inning-1])
    except :
        return findhomeruns(st[inning+1:], i)

    return findhomeruns(st[inning+4:], i+1)
    
# 총정리
def details(yyyymm, score):
    tmp = []
    (year, month) = ym(yyyymm)

    teams = {"두산" : "OB", "키움" : "WO", "넥센" : "WO", "SK" : "SK", "LG" : "LG", \
             "NC" : "NC", "KT" : "KT", "kt" : "KT", "KIA" : "HT", "삼성" : "SS", \
             "한화" : "HH", "롯데" : "LT"}
    for (month, date, away, home, double, stadium) in score :
        month = str(month)
        date = str(date)
        if len(month) == 1 :
            month = '0'+month
        if len(date) == 1 :
            date = '0'+date
        url = geturl2(year, month, date, teams[away], teams[home], double)
        st = cuturl2(url)
        if len(st) == 0 :
            n = 0
        else :
            n = findhomeruns(st, 0)
        tmp.append((year, month, date, n, stadium))
    return tmp

# pandas에 저장
def regist(x, df):
    for (year, month, date, n, stadium) in x:
        l = len(df["YEAR"])
        df.loc[l] = [int(year), int(month), int(date), n, stadium]



base_dir = 'C:/Users/Username/Desktop/'
excel_file = "homeruns.xlsx"
excel_dir = os.path.join(base_dir, excel_file)
df = pd.read_excel(excel_dir, encoding="EUC_KR")


year_list = [(200803, 200811), (200904, 200911), (201003, 201011), (201104, 201111), (201204, 201212), \
            (201303, 201312), (201403, 201412), (201503, 201511), (201603, 201612), (201703, 201711), \
            (201803, 201812), (201903, 201911)]

for (date_start, date_end) in year_list:
    for date in range(date_start, date_end):
        url = geturl(date, "")
        st = cuturl(url)
        ls = cutst(st)
        sc = score(ls)
        x = details(date, sc)
        print(str(date)+" OK")
        regist(x, df)


df.to_excel(excel_file, index = False)
print("Saved!")
728x90
반응형