파이썬 크롤링 예제, 메이저리그 경기 데이터 수집하기

2019. 8. 16. 06:30

데이터 분석 및 모델링을 위해서 가장 중요한 것은 뭐니뭐니해도 데이터이다. 하지만 일반인들이 분석 및 모델링을 위한 데이터를 수집하기는 쉽지 않다. 그런 의미에서 웹크롤링은 굉장히 유용하다. 웹에 많은 데이터들이 있고 빠르고 쉽게 수집할 수 있기 때문이다. 웹크롤링을 하지 않기 수작업으로 한땀한땀 수집할 수도 있지만, 5분이면 할 일을 50분에 걸쳐서 하기 때문에 굉장히 비효율적이다.

오늘은 파이썬 크롤링 예제, 메이저리그 경기 데이터 수집하기에 대해서 알아보았다.

 

파이썬_크롤링

 

 

웹크롤링을 애기할 때 크게 2가지 방법이 있는 듯 하다.

 

하나는 책에서도 많이 나오는 http(hyper text transfer protocol)를 이용하여 필요한 웹페이지의 HTML을 수집한다. 그리고 Beautiful soup(뷰티풀숩)을 이용해서 파싱하여 원하는 데이터를 추출한다.
( 참조: 웹크롤링 필수 라이브러리 beautifulsoup 사용법과 유용한 팁 )

 

다른 하나는 sellenium(셀레니움)을 이용하는 방법이다. 셀레니움은 웹테스트를 자동화하기 위해 만들어진 라이브러리이다. HTML을 수집하고 뷰티풀숩을 이용해서 하는 것은 동일하다. 차이점음 셀레니움을 통해서 데이터를 얻기 위해 버튼을 누른거나 텍스트를 입력하는 부분들을 자동화하는 것이다. 앞의 방법보다는 한 단계 더 나아간 방법이라고 보면 되겠다. 셀레니움에 대한 자세한 내용은 아래 포스팅을 참조해보자.
( 참조: 파이썬 셀레니움(selenium) 이용 네이버 자동로그인 하기 )

 

 

첫 번째 방법은 과거에 경마 경기 데이터를 수집하면서 포스팅을 하였다. ( 참조: 경마 데이터 수집하기 - 경주 결과 웹크롤링 하기 ) 오늘은 셀레니움을 이용해서 크롤링을 하는 방법에 대해서 알아보도록 하겠다.

 

수집한 데이터는 메이지리그 18년 경기 투구・타격 통계와 결과 데이터이다. 혹시 데이터가 필요하다면 아래 데이터를 참조하면 되겠다.

 

메이저리그_2018년_경기결과.csv
다운로드

 

메이저리그_2018년_타격기록.csv
다운로드

 

메이저리그_2018년_투구기록.csv
다운로드

 

18년 경기 투구・타격 통계 데이터는 메이저리그 홈페이지에 공개되어 있다. API로도 제공되는지는 모르겠지만, 홈페이지에 공개된 데이터로도 충분할 듯 하다.

 

메이저리그_홈페이지
메이저리그-홈페이지

 

 

 

수집하고자 하는 데이터는 월별 타격과 투구에 대한 데이터이다. 월별 데이터는 URL에 파라미터로 들어가 있기 때문에 이 값을 변경하면 된다. 현재 페이지의 URL은 아래와 같다.

 

http://mlb.mlb.com/stats/sortable.jsp#elem=%5Bobject+Object%5D&tab_level=child&click_text=Sortable+Player+pitching&game_type='R'&season=2018&season_type=ANY&league_code='MLB'&sectionType=sp&statType=pitching&page=1&ts=1565876169032&playerType=ALL&sportCode='mlb'&split=3&team_id=&active_sw=&position=&page_type=SortablePlayer&sortOrder='desc'&sortColumn=avg&results=&perPage=50&timeframe=&last_x_days=&extended=0

 

중간에 보면 split은 3이라고 되어 있는데 이 부분을 4로 바꾸면 4월 데이터가 조회된다. 5로 바꾸면 5월 데이터가 조회된다. 이렇게 URL의 파리미터를 바꾸면서 크롤링을 하면 된다.

 

하지만 문제(?)가 하나 있다. 한 페이지에 데이터가 다 안 나와 있다는 것이다. 위에 그림에 보면 Next Stats라는 버튼을 눌러서 그 뒤에 있는 데이터까지 수집해야 한다. 그리고 아래에 보면 각 월별로 여러개의 페이지가 있는 것을 알 수 있다.

 

숫자

( 월별로 여러 개의 페이지의 데이터를 모아야 한다 )

 

이러한 버튼들을 누르는 작업은 셀레니움을 이용해서 하려고 한다. 셀레니움을 사용하기 위해서는 크롬드라이버를 설치해야 한다.

 


import pandas as pd

from selenium import webdriver

from bs4 import BeautifulSoup

from html_table_parser import parser_functions as parser

import time

 

browser = webdriver.Chrome('C:\Program Files (x86)\Google\Chrome\Application\chromedriver')

 

 

셀레니움으로 웹브라저를 실행하기 위해서 크롬드라이브의 위치를 지정한다. 

 


month = list([3,4,5,6,7,8,9,10])

 

for m in month:

    browser.get("http://mlb.mlb.com/stats/sortable.jsp#elem=%5Bobject+Object%5D&tab_level=child&
    click_text=Sortable+Team+hitting&game_type='R'&season=2018&season_type=ANY&league_code=
    'MLB'&sectionType=st&statType=hitting&page=1&ts=1564111119079&playerType=ALL&sportCode=
    'mlb'&split="+str(m)+"&team_id=&active_sw=&position=&page_type=SortablePlayer&sortOrder=
    'desc'&sortColumn=avg&results=&perPage=50&timeframe=&last_x_days=&extended=0")

    html = browser.page_source

    soup = BeautifulSoup(html, 'html.parser')

    table_tags = soup.find_all("table")

    table = table_tags[0]

    p=parser.make2d(table)

 

    df=pd.DataFrame(p[1:],columns=p[0])

    df["month"] = 201800 + m

    mlb_record_hit1_2018 = pd.concat([mlb_record_hit1_2018, df], 0)

 

get함수를 이용하면 특정 홈페이지를 크롬에서 열 수 있다. page_source파라미터를 이용하면 HTML을 가져올 수 있다. 이후에는 일반적으로 웹크롤링을 하는 것처럼 뷰티풀숩으로 HTML을 파싱하고 필요한 데이터를 가져오면 된다. 가져와야 하는 월들은 list에 저장하여 for문을 돌렸다.

 

전체 페이지수를 가져오기 위해서 아래와 같이 button 태그가 들어가 있는 모든 텍스트를 가져왔다. 이 중에 세 번째 테그가 우리가 원하는 숫자이므로 해당 값을 가져와서 num이라는 변수에 저장했다.

 


    num = soup.find_all("button")[3].text

 

이제 다음 버튼을 누르면서 데이터를 수집해야 한다. 크롬 브라우저의 F12버튼을 누르면 개발자 모드로 진입한다. 여기서 다음 버튼의 태그 정보를 확인할 수 있다. 해당 태그의 클래스 이름은 'paginationWidget-next'이다. 해당 태그를 btn이라는 변수에 저장하고, click함수를 호출하면 버튼을 클릭할 수 있다.

개발자모드를 통해 태그 정보를 확인하는 방법이 궁금하다면 아래 포스팅을 참조하기 바란다.
( 참조: 경마 데이터 수집하기 - 경주 결과 웹크롤링 하기 )

 

   
    btn = browser.find_element_by_class_name('paginationWidget-next')

    btn.click()

 

 

이제 위의 데이터를 이용해서 필요한만큼 for문을 돌리면 필요한 데이터를 가져올 수 있다. 위의 내용을 정리해보면 아래와 같다.

 


mlb_record_pitching_2018 = pd.DataFrame()

 

month = list([3,4,5,6,7,8,9,10])

 

for m in month:

    browser.get("http://mlb.mlb.com/stats/sortable.jsp#elem=%5Bobject+Object%5D&tab_level=child&
    click_text=Sortable+Player+pitching&game_type='R'&season=2018&season_type=ANY&league_code
    ='MLB'&sectionType=sp&statType=pitching&page=1&ts=1564237190257&playerType=ALL&sportCode
    ='mlb'&split="+str(m)+"&team_id=&active_sw=&position='1'&page_type=SortablePlayer&sortOrder
    ='asc'&sortColumn=era&results=&perPage=50&timeframe=&last_x_days=&extended=0")

    time.sleep(5)

 

    html = browser.page_source

    soup = BeautifulSoup(html, 'html.parser')

 

    num = soup.find_all("button")[3].text

 

    # 첫 페이지 데이터 테이블에 한 번 넣기 

    table_tags = soup.find_all("table")

    table = table_tags[0]

    p=parser.make2d(table)

 

    df=pd.DataFrame(p[1:],columns=p[0])

    df["month"] = 201800 + m

    mlb_record_pitching_2018 = pd.concat([mlb_record_pitching_2018, df], 0)

 

 

    for i in range(1,int(num)):

        btn = browser.find_element_by_class_name('paginationWidget-next')

        btn.click()

        time.sleep(5)

 

        # 2페이지 이후 데이터 넣기

        html = browser.page_source

        soup = BeautifulSoup(html, 'html.parser')

        table_tags = soup.find_all("table")

        table = table_tags[0]

        p = parser.make2d(table)

 

        df = pd.DataFrame(p[1:], columns=p[0])

        df["month"] = 201800 + m

        mlb_record_pitching_2018 = pd.concat([mlb_record_pitching_2018, df], 0)

 

 

 

오늘은 이렇게 파이썬 크롤링 예제, 메이저리그 경기 데이터 수집하기에 대해서 알아보았다. 셀레니움을 통해서 필요한 정보를 추출하고 웹브라우저를 컨트롤해서 데이터를 수집할 수 있었다. 셀레니움을 통한 웹자동화는 여러 면에서 효율적인만큼 잘 활용하면 도움이 많이 될 듯 하다.

댓글()
  1. soil333@naver.com 2020.01.20 18:26 댓글주소  수정/삭제  댓글쓰기

    안녕하세요~
    파이썬에 참 좋은 파이참 책을 사서 읽고 있는 독자입니다.
    mlb데이터를 다운 받았는데요 경기결과 파일은 글자가 깨져있네요(바이너리인듯요)
    어떻해 해결해야 하나요?

    • ✑ 테리엇 2020.01.21 20:02 신고 댓글주소  수정/삭제

      한글 인코딩이 안 맞는 것 같습니다. 한글 인코딩이 UTF-8로 되어 있거든요. 파일을 읽을 때 인코딩을 지정해서, 아래처럼 해보시겠어요?
      import pandas as pd
      df = pd.read_csv("메이저리그_2018년_경기결과.csv", encoding="UTF-8")
      df.head()

  2. 투자하는 복슬강아지 2020.06.29 08:11 신고 댓글주소  수정/삭제  댓글쓰기

    좋은 글 감사합니다.