프로젝트/[DA] 데이터 분석 : 배구

데이터 수집 (웹 크롤링) (2) - 경기 데이터

sollllllll 2022. 3. 26. 23:45

예측 모델을 만들어보려고 하는데 기존에 모았던 데이터로는 모델을 만들기에 적합하지 않다고 판단했다.

그래서 경기 관련 데이터를 추가로 수집했는데 이번 포스트에서는 그 내용을 담아보려고 한다.

자세한 크롤링 방법은 이전 포스트를 참고하면 된다.

 

 

 

(1) 수집할 데이터 담은 태그 찾기

저번과 같은 방식으로 크롤링을 할 것이기 때문에 먼저 url과 태그를 살펴보도록 하겠다.

크롤링을 수행할 페이지는 다음과 같다. KOVO 공식 홈페이지의 경기 결과 페이지이다.

경기 결과 페이지

페이지를 살펴보면 경기 날짜, 경기 번호(순번), 경기를 치른 두 팀, 경기 결과, 경기가 진행된 라운드(정규리그는 총 6라운드로 구성된다.)의 데이터를 볼 수 있다.

 

url은 "https://www.kovo.co.kr/game/v-league/(생략)season=018&team=&s_part=2&yymm=&r_round=1"으로,

라운드 별로 데이터를 수집하기 위해서 라운드 조건을 설정한 것이 url에 반영된 것을 볼 수 있다.

 

이어서 태그를 보기 위해 개발자 도구 창을 열었다.

페이지 코드

저번 경우와 마찬가지로 데이터가 테이블 안에 저장되어 있고 그 아래 tbody, tr, td 순으로 하위로 뻗어나가는 것을 확인할 수 있었다.

 

 

 

 

(2) 코드 작성

이제 찾은 클래스와 태그를 바탕으로 크롤러 코드를 작성하도록 하겠다.

저번처럼 크롤러를 돌릴 때마다 input 메소드로 수집할 라운드를 설정한 후 크롤링하는 방법과 반복 범위를 지정한 후 크롤링하는 방법을 선택할 수 있다.

정규리그 전체가 6라운드이기 때문에 이번에는 반복 횟수를 정해서 크롤링했다.

for i in range(1,7):
    search_url = "https://www.kovo.co.kr/game/v-league/11110_schedule_list.asp?season=018&team=&s_part=2&yymm=&r_round="+str(i)

    browser.get(search_url)
    browser.implicitly_wait(2)

    # 크롤링 할 데이터 테이블,태그 찾기 - 경기 정보 (날짜, 홈팀, 원정팀, 경기결과, 라운드)
    table = browser.find_element_by_class_name('wrp_lst')    # div.wrp_lst
    tbody = table.find_element_by_tag_name("tbody")
    rows = tbody.find_elements_by_tag_name("tr")
    
...

이번에도 table 을 찾을 때 table 태그 위의 div 태그의 클래스명을 사용했는데, 테이블 클래스명을 지정했을 때 오류가 발생했기 때문이다.

코드에 적용하진 않았지만 tbody 나 rows 처럼 tag_name 으로 table 을 찾는 방법도 가능한데, 정확하게 수행하기 위해서 table을 찾을 때는 클래스명을 사용했다.

 

 

이번에는 tr 안의 데이터들을 저장하는 과정이 길어졌는데, 하나씩 살펴보도록 하겠다.

위의 웹 페이지를 보면 테이블 안에 중간 중간 경기가 없는 날이 있다.

하지만 크롤링을 수행하다보면 경기가 없는 날의 데이터도 rows 에 포함되었기 때문에 조건문을 통해 해당 경우를 제외했다.

 

tr 안의 데이터를 통해 수집할 데이터는 총 7가지로 [날짜, 경기번호, 홈팀, 원정팀, 스코어, 승리팀, (경기한)세트 수] 이다.

하지만 원래의 데이터를 그대로 저장할 수는 없고 약간의 가공을 거쳐야 한다.

 

10월 16일의 데이터를 예시로 설명하면

date 데이터는 가공 과정을 거치면 '10. 16 (토)'의 형태에서 '1016' 의 형태로 바뀌고

teamH 데이터는 'GS칼텍스  3 :' 에서 'GS칼텍스' 형태로, teamA 데이터는 '0  흥국생명' 에서 '흥국생명'으로 바뀐다.

score 데이터는 초기의 teamH, teamA 데이터를 합한 후 숫자와 ':' 문자를 리스트로 추출한 후 하나의 문자열로 바꾸고

경기를 이기는 조건인 3세트를 따낸 팀이 홈팀이면(index 0) win 데이터에 홈팀을, 그 외에는 원정팀을 저장한다.

score 데이터를 리스트로 변환할 때 ':' 문자가 포함되었기 때문에 총 세트 수를 구할 때는 숫자만 추출해서 더한다.

(데이터를 리스트에 추가하는 과정은 코드에 포함하지 않았다. *이전 포스트 참고)

for value in rows:
    d = value.find_elements_by_tag_name("td")
    if len(d)>3:
        date = d[0].text
        ndate = re.findall('[0-9]',date)
        ndate = "".join(date)
        gamenum = d[1].text
        teamH = d[3].text
        teamA = d[4].text
        score = re.findall('[0-9:]',teamH+teamA)
        teamH = re.sub('[0-9: ]', '', teamH)
        teamA = re.sub('[0-9: ]', '', teamA)
        if score.index('3')==0:
            win = teamH
        else:
            win = teamA
        score = "".join(score)
        setnum = re.findall('[0-9]',teamH+teamA)
        setcnt = 0
        for s in setnum:
            setcnt += int(s)
          
...

 

 

 

 

(3) 데이터 저장

크롤링이 끝난 후에는 마찬가지로 csv 파일로 데이터를 저장한다.

수집한 데이터 중 날짜, 홈팀, 원정팀, 스코어, 라운드, 승리팀을 하나의 데이터프레임으로 변환한 모습이다.

경기 데이터

 

위 데이터는 자세히 보면 날짜가 가공한 형태가 아닌 원래의 형태인데 그렇다면 가공한 날짜 데이터와 함께 수집한 경기 번호, 총 세트 수 데이터는 왜 수집한 걸까?

세 데이터는 다음 포스트에서 사용할 예정인데 위 데이터는 경기의 대략적인 데이터를 만든 것이고, 다른 웹 페이지를 통해 경기 세부 데이터를 추가로 모을 예정이기 때문에 그것에 필요한 새 날짜, 경기 번호, 세트 수 데이터를 함께 모은 것이다.

 

 

 

어쩌다보니 크롤링 관련해서 게시글을 세 개나 작성하게 되었는데,

그만큼 관련 내용도 많이 찾아보고 수행해보면서 많은 것을 깨달아가고 있어서 뿌듯하긴 하다.

그럼 이어지는 포스트에서는 경기 세부 데이터를 수집해보도록 하겠다.