[OPGG] 특정 유저, 시즌별 챔피언 정보

Updated:

1. 시즌 챔피언 정보

앞서 배웠던 웹 스크래핑 내용을 토대로 특정 유저의 특정 시즌 랭크 플레이 챔피언 정보를 스크래핑 해보았다.

import time
import pandas as pd

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
# headless selenium
options = webdriver.ChromeOptions()
options.add_argument('headless')
options.add_argument('window-size=1920x1080')
def my_champion(userName, season):
    # 빈 데이터 프레임
    data = pd.DataFrame()
    
    # 띄어쓰기 있는 유저이면 +로 구분
    userName2 = "+".join(userName.split())

    # 유저별 검색 결과 url
    url = f"https://www.op.gg/summoner/userName={userName2}"

    # driver 실행
    # driver = webdriver.Chrome("./chromedriver")
    driver = webdriver.Chrome("./chromedriver", options= options)
    driver.get(url)
    time.sleep(1)

    # 챔피언 클릭 후 다음 창 로드까지 잠시 대기
    driver.find_element_by_id("left_champion").find_element_by_css_selector("a").click()
    time.sleep(1)

    # 원하는 시즌 설정 (xpath의 li[index]를 사용: index가 1부터 시작)
    # 시즌이 추가될 때마다 수정해주어야 함
    season_dict = {
        2021:"tabItem season-17",
        2020:"tabItem season-15",
        2019:"tabItem season-13",
        2018:"tabItem season-11",
        2017:"tabItem season-7",
        2016:"tabItem season-6",
        2015:"tabItem season-5",
        2014:"tabItem season-4",
        2013:"tabItem season-3",
        2012:"tabItem season-2",
        2011:"tabItem season-1"
    }

    season2 = list(season_dict.keys()).index(season) + 1

    # 시즌 클릭 (모두 클릭해야 정보가 나타나므로)
    for i in range(len(season_dict)):
        driver.find_element_by_xpath(f"//*[@id='champion_season']/li[{i+1}]/a").click()

    # url 정보 저장
    soup = BeautifulSoup(driver.page_source, "lxml")
    driver.quit()

    # 플레이한 챔피언 데이터 테이블
    champ_info = soup.find("div", attrs= {"class":f"{season_dict[season]}"})
    champ_info2 = champ_info.find("tbody", attrs= {"class":"Body"})
    champ_info2 = champ_info2.find_all("tr")
    
    # 각 태그 지정해서 원하는 정보 추출
    for i in range(len(champ_info2)):
        lst = []
        # 챔피언 이름
        lst.append(champ_info2[i].find("td", attrs= {"class":"ChampionName Cell"}).get_text().strip())

        # 승/패 (단, 한번도 이기거나 진적이 없으면 해당 class가 없음)
        if champ_info2[i].find("div", attrs= {"class":"Text Left"}) == None:
            lst.append(0)
        else:
            lst.append(champ_info2[i].find("div", attrs= {"class":"Text Left"}).get_text().strip()[:-1])

        if champ_info2[i].find("div", attrs= {"class":"Text Right"}) == None:
            lst.append(0)
        else:
            lst.append(champ_info2[i].find("div", attrs= {"class":"Text Right"}).get_text().strip()[:-1])

        # K/D/A
        KDA = champ_info2[i].find("div", attrs= {"class":"KDA"}).get_text().strip().split()
        lst.append(KDA[0])
        lst.append(KDA[2])
        lst.append(KDA[4])
        
        # 기타 정보
        else_info = champ_info2[i].find_all("td", attrs={"class":"Value Cell"})
        for i in else_info:
            info = i.get_text().strip().split()
            if len(info) == 0:
                lst.append(0)
            else:
                lst.append(info[0])
        
        # 데이터 프레임
        champ_df = pd.DataFrame(lst)
        data = pd.concat([data, champ_df], axis=1)
    
    # 데이터 프레임 형태, 타입 설정
    data = data.T
    data.index = range(data.shape[0])
    data.columns = [
        "champion", "win", "lose", "kill", "death", "assist", 
        "gold", "cs", "max_kill", "max_death", "avg_damage", "avg_damaged", "double", "triple", "quadra", "penta"
    ]
    
    data["win"] = data["win"].astype(int)
    data["lose"] = data["lose"].astype(int)
    data["kill"] = data["kill"].astype(float)
    data["death"] = data["death"].astype(float)
    data["assist"] = data["assist"].astype(float)
    data["gold"] = data["gold"].apply(lambda x: int(x.replace(",","")))
    data["cs"] = data["cs"].astype(float)
    data["max_kill"] = data["max_kill"].astype(int)
    data["max_death"] = data["max_death"].astype(int)
    data["avg_damage"] = data["avg_damage"].apply(lambda x: int(x.replace(",","")))
    data["avg_damaged"] = data["avg_damaged"].apply(lambda x: int(x.replace(",","")))
    data["double"] = data["double"].astype(int)
    data["triple"] = data["triple"].astype(int)
    data["quadra"] = data["quadra"].astype(int)
    data["penta"] = data["penta"].astype(int)
    
    return data
  • 유저 이름에 띄어쓰기가 있으면 검색시 URL이 userName=Hide+on+bush과 같이 +로 구분되어 이를 반영

  • 챔피언 탭 클릭 후 각 시즌을 모두 클릭해야만 해당 정보가 생성되므로 모두 클릭(노말 제외)

  • 시즌 입력시 연도로 입력하고 이를 URL 반영하기 위해 dictionary를 생성

  • 만약 특정 챔피언이 모두 승이거나 모두 패이면 해당 class를 가지는 div 태그가 없음

  • 더블킬, 트리플킬 등의 경우는 없으면 해당 class의 태그는 존재하지만 텍스트가 빈 값으로 입력되있음

OP.GG: 현재 리그오브레전드 공식 API 서버의 문제로 인해 최근 게임 데이터 갱신이 조금 늦을 수 있습니다.

위 메시지 이후로 챔피언 탭 클릭 명령어

driver.find_element_by_id("left_champion").find_element_by_css_selector("a").click()

가 될 때도 있고 안 될때도 있음 (확실한 이유는..?)

# 원하는 유저의 특정 시즌 챔피언 정보
userName = "Hide on bush"
season = 2020

result = my_champion(userName, season)
result
champion win lose kill death assist gold cs max_kill max_death avg_damage avg_damaged double triple quadra penta
0 사일러스 55 60 6.0 4.3 5.6 11307 202.5 16 13 124393 25284 78 14 4 0
1 조이 54 50 5.7 3.3 6.8 11623 210.0 19 9 136912 13779 53 6 2 1
2 루시안 53 51 5.0 4.1 5.3 12133 234.6 15 11 156282 16874 44 5 2 0
3 아칼리 37 47 6.5 4.0 4.2 11326 209.8 24 9 121981 21255 65 9 0 0
4 세트 47 26 4.5 3.0 6.5 10649 187.1 13 7 112374 24356 30 3 0 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
95 신 짜오 0 1 7.0 8.0 15.0 13612 177.0 7 8 181530 52539 1 0 0 0
96 룰루 0 1 2.0 6.0 20.0 11502 183.0 2 6 107024 21541 0 0 0 0
97 0 1 0.0 3.0 0.0 5193 119.0 0 3 37514 8179 0 0 0 0
98 스웨인 0 1 8.0 5.0 7.0 14101 213.0 8 5 140346 36175 2 0 0 0
99 제라스 0 1 4.0 6.0 10.0 9509 33.0 4 6 62670 16061 0 0 0 0

100 rows × 16 columns

Leave a comment