ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 라이엇 api를 활용한 리그오브레전드 데이터 수집
    ML, DL & Python/Riot API를 활용환 데이터 분석 2019. 6. 1. 19:36

     

     

    안녕하세요.

    이번 포스팅은 평소에 즐겨하던 "리그오브레전드"의 게임 데이터를 이용하여 분석해보는 시간을 갖도록 하겠습니다.

     

    그전에 리그오브레전드 데이터를 수집해야하는데요, 리그오브레전드를 즐겨하거나 해보신 분들은 알겠지만 리그오브레전드의 게임 정보나, 챔피언 정보를 알 수 있는 사이트인 OP.GG를 알고 계실 것입니다.

    OP.GG도 마찬가지로 Riot api를 이용하여 데이터를 수집해 유저에게 유용한 정보를 제공하고 있습니다. 여기서 중요한 것은 API로 제공하는 데이터는 게임을 이용하는 소환사별로 최근 20게임만을 저장하고 있는 것인데 OP.GG에서는 소환사별로 20게임보다 훨씬 이전의 데이터까지 볼 수 있다는 것입니다.

    이는 OP.GG자체에서 DB에 저장하고 있다는 것을 알 수 있습니다.(엄청난 노력을 하고 있는 것인...)

     

    따라서 저는 OP.GG만큼은 아니지만 기본적인 소환사 정보와 Grand Master랭크를 지니고 있는 소환사들의 경기 데이터를 수집하여 분석을 진행하도록 하겠습니다.

     

    이번 포스팅에서는 일단 API로 데이터를 수집하는 것을 중점적으로 다루겠습니다.

     

    1. Riot API Key발급 받기

     

    Riot Developer Portal

    About the Riot Games API With this site we hope to provide the League of Legends developer community with access to game data in a secure and reliable way. This is just part of our ongoing effort to respond to players' and developers' requests for data and

    developer.riotgames.com

    위의 라이엇 개발자 사이트 접속을 합니다. 그리고 기존에 가지고 계시거나 회원가입을 새로 하셔서 로그인을 한 뒤에 오른쪽 상단 아이디가 써있는 개인정보의 DashBoard를 들어가면 발급받은 Riot api키가 있을 것입니다.

     

    주의하실것은 Riot측에서 key발급을 무조건적으로 해주지 않는다는 것입니다. 꼭! 이메일 인증을 받으셔야 됩니다.

     

    이렇게 api key를 받으셨다면 데이터를 이용할 준비가 되신겁니다.

    한번 받은 api가 제대로 된 것인지 확인해봅시다.

    위의 이미지 순서대로 이동한 후에 내가 알고 있는, 내 소환사아이디를 입력합니다. 저는 페이커선수의 hide on bush를 입력했습니다.

     

    저는 request모듈을 사용하여 api에 접근해서 데이터를 받았습니다.

     

    이때 앞으로 request하여 응답을 제대로 받았는지 확인하기 위해서 reponse에 써있는 숫자를 확인합니다. 200이면 성공!

     

    하지만 여기서 끝이 아니라 소환사 정보를 통해서 소환사 고유 id를 이용하여 랭크정보를 수집해야 합니다.

    이쯤에서 알 수 있듯이 Riot api는 데이터를 수집하기 위해서 이전에 수집한 정보를 연계하여 이용해 정보를 수집해야 하는 것입니다. 여기서도 마찬가지로 소환사 닉네임을 통해서 소환사 고유 id를 수집하여 소환사별 랭크를 수집하는 것입니다.

    api_key = '발급받은 api'
    sohwan = "https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/" +'hide on bush' +'?api_key=' + api_key
    r = requests.get(sohwan)
    r.json()['id'] #소환사의 고유 id
    

     

    2. 수집한 소환사 고유 id를 이용하여 소환사 랭크정보 수집(이 단계는 4단계에서 필요하니 숙지해주시면 됩니다.)

    소환사 랭크 정보는 League의 8번째 탭을 들어가면 얻을 수 있습니다.

    *수정버전

    제가 그때 당시 api 이전 버전을 기준으로 글을 작성했는데 현 api 버전에서는 v4/positions/by-summoner/{encryptedSummonerId}가 사라지고 v4/entries/by-summoner/{encryptedSummonerId}로 바뀌었네요.(league-v4의 2번째 탭에 있습니다)

    이 부분을 수정하신 뒤에 수집을 이어나가시면 될 것 같습니다! 불편끼쳐드려 죄송합니다 ㅠㅠ

    tier_url = "https://kr.api.riotgames.com/lol/league/v4/positions/by-summoner/" + r.json()['id'] +'?api_key=' + api_key
    r2  = requests.get(tier_url)
    r2.json()

     

    페이커 선수의 랭크정보

    위의 데이터로 알 수 있는 정보는 페이커선수는 hide on bush라는 소환사 닉네임을 사용하며 티어(랭크)는 그랜드 마스터이고 자유랭크는 하지 않고 솔로랭크 5:5의 랭크만을 가지고 있는 것입니다. 또한 승리는 229회 패배는 239회이며 랭크 점수는 459점인 것을 알 수 있습니다.

     

    따라서 이와 같은 정보는 OP.GG에서 가장 중요한 "소환사 검색"에서 주로 사용하는 API임을 알 수 있습니다. 위의 두개의 API를 이용하여 아주 간단한 전적만들기 사이트를 이후 포스팅에서 Django로 구현해보도록 하겠습니다.

     

    3. 원하는 리그의 소환사 데이터 수집(Challenger, GrandMaster, Master...)

    어느 게임이든 가장 중요한 것은 내부에서 경기가 어떤 식으로 이루어져있고 승패가 어떻게 갈렸는가?가 굉장히 중요할 것입니다. 따라서 경기 기록 데이터를 수집해보도록 하겠습니다.

    경기기록은 그랜드마스터 랭크의 게임데이터를 수집했으며 데이터의 갯수는 17500개입니다.

     

    먼저 League탭에서 그랜드마스터 api를 클릭합니다.

    그랜드마스터의 소환사 고유id를 불러옵니다.

    grandmaster = 'https://kr.api.riotgames.com/lol/league/v4/grandmasterleagues/by-queue/RANKED_SOLO_5x5?api_key=' + api_key
    r = requests.get(grandmaster)#그마데이터 호출
    league_df = pd.DataFrame(r.json())
    
    league_df.reset_index(inplace=True)#수집한 그마데이터 index정리
    league_entries_df = pd.DataFrame(dict(league_df['entries'])).T #dict구조로 되어 있는 entries컬럼 풀어주기
    league_df = pd.concat([league_df, league_entries_df], axis=1) #열끼리 결합
    
    league_df = league_df.drop(['index', 'queue', 'name', 'leagueId', 'entries', 'rank'], axis=1)
    league_df.info()
    league_df.to_csv('그마데이터.csv',index=False,encoding = 'cp949')#중간저장

    수집한 소환사 닉네임

     

    4. 소환사 고유 닉네임(summonerName)를 이용하여 유저별 고유 accountId 수집

    이 부분은 Summoner API에서 summonername을 사용하던 summonerId를 사용하던 상관없습니다. 저희는 유저의 accountId만 있으면 되기 때문입니다.

     

    이렇게 불러온 소환사 닉네임을 통해서 Step1과 Step2를 숙지한 것처럼 그랜드마스터랭크의 소환사 고유 id(accountId)를 수집합니다.

    for i in range(len(league_df)):
        try:
            sohwan = 'https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/' + league_df['summonerName'].iloc[i] + '?api_key=' + api_key 
            r = requests.get(sohwan)
            
            while r.status_code == 429:
                time.sleep(5)
                sohwan = 'https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/' + league_df['summonerName'].iloc[i] + '?api_key=' + api_key 
                r = requests.get(sohwan)
                
            account_id = r.json()['accountId']
            league_df.iloc[i, -1] = account_id
        
        except:
            pass
    

     

    5. 매치데이터 수집

    매치데이터는 수집하는데 오랜시간이 걸렸습니다. 이유는 라이엇측에서 개발자 api를 이용하는데 이용량의 한계를 걸어놨기 때문에 지속적으로 수집이 불가능합니다. 따라서 time모듈을 활용하여 수집하거나 끊어서 수집을 해야하기 때문에 수집하는데 시간이 오래걸립니다. 이 부분은 제가 RIot API를 수집하면서 생긴 에러를 모두 예외처리한 코드를 샘플로 올려드릴테니 꼭 참고 부탁드리겠습니다!!

     

    경기기록 데이터는 MATCH탭의 1번째 API에 존재하며 이후에는 매치데이터로 정의하겠습니다. 먼저 매치 데이터를 수집하기 위해서는 선행프로세스가 존재합니다. 

     

     

    위 사진의 matchid를 얻기 위해서는 그 밑의 두번째 get api를 사용해야합니다. 두번째 api는 어떤 정보가 있어야 수집할 수 있는가? 그것은 바로 바로 직전에 수집한 accountid를 이용하여 수집할 수 있습니다. API가 필요한 토큰에 encryptedAccountId가 있죠? 그 부분이 저희가 4단계에서 수집한 accountId가 들어갈 자리입니다.

     

    #13시즌의 데이터만을 이용할 것이며 league_df3 => 기존에 수집한 account_id가 있는 league_df
    match_info_df = pd.DataFrame()
    season = str(13)
    for i in range(len(league_df3)):
        try:
            match0 = 'https://kr.api.riotgames.com/lol/match/v4/matchlists/by-account/' + league_df3['account_id'].iloc[i]  +'?season=' + season + '&api_key=' + api_key
            r = requests.get(match0)
            
            while r.status_code == 429:
                time.sleep(5)
                match0 = 'https://kr.api.riotgames.com/lol/match/v4/matchlists/by-account/' + league_df3['account_id'].iloc[i]  +'?season=' + season + '&api_key=' + api_key
                r = requests.get(match0)
                
            match_info_df = pd.concat([match_info_df, pd.DataFrame(r.json()['matches'])])
        
        except:
            print(i)
    

    gameid(matchid)수집완료

    자 gameId(matchId) 까지 수집하는 과정은 크게 시간소요가 걸리지 않을 것입니다. 하지만 Match data는 고려해야할 부분(예외처리)이 있으니 집중해주세요!

     

    6. MatchId(gameId)를 활용한 최종 경기 데이터 수집

    gameid를 중복제거한 이후에 gameid를 이용하여 게임 내부의 데이터를 본격적으로 수집해보도록 하겠습니다.

    for i in range(len(match_info_df2)):    
        
        api_url='https://kr.api.riotgames.com/lol/match/v4/matches/' + str(match_info_df2['gameId'].iloc[i]) + '?api_key=' + api_key
        r = requests.get(api_url)
    
        if r.status_code == 200: # response가 정상이면 바로 맨 밑으로 이동하여 정상적으로 코드 실행
            pass
    
        elif r.status_code == 429:
            print('api cost full : infinite loop start')
            print('loop location : ',i)
            start_time = time.time()
    
            while True: # 429error가 끝날 때까지 무한 루프
                if r.status_code == 429:
    
                    print('try 10 second wait time')
                    time.sleep(10)
    
                    r = requests.get(api_url)
                    print(r.status_code)
    
                elif r.status_code == 200: #다시 response 200이면 loop escape
                    print('total wait time : ', time.time() - start_time)
                    print('recovery api cost')
                    break
    
        elif r.status_code == 503: # 잠시 서비스를 이용하지 못하는 에러
            print('service available error')
            start_time = time.time()
    
            while True:
                if r.status_code == 503 or r.status_code == 429:
    
                    print('try 10 second wait time')
                    time.sleep(10)
    
                    r = requests.get(api_url)
                    print(r.status_code)
    
                elif r.status_code == 200: # 똑같이 response가 정상이면 loop escape
                    print('total error wait time : ', time.time() - start_time)
                    print('recovery api cost')
                    break
        elif r.status_code == 403: # api갱신이 필요
            print('you need api renewal')
            print('break')
            break
    
        # 위의 예외처리 코드를 거쳐서 내려왔을 때 해당 코드가 실행될 수 있도록 작성
        mat = pd.DataFrame(list(r.json().values()), index=list(r.json().keys())).T
        match_fin = pd.concat([match_fin,mat])
    
    

     

     

    코드에서 보시는 것처럼 예외처리를 해주고 Riot API cost가 항상 제한되어 있기 때문에 429error, 503error가 나왔을 때 항상 시간텀을 무한루프로 돌게끔 만들어서 수집하도록 만들었습니다.

     

    우리가 사용할 데이터는 기본적으로 승패예측을 할 것이기 때문에 경기시간(Duration), 각 팀의 경기 내부 기록(teams),소환사 즉, 게임 참여자들의 정보(participants)를 이용할 것입니다.

     

    7. 최종 Team, GameDuration을 병합하여 데이터 셋 구축

    duration데이터는 단일데이터 상태로 존재하는데 teams와 participants컬럼 데이터는 리스트안에 딕셔너리 구조로 되

    어 있다. 따라서 안에 들어 있는 딕셔너리 데이터를 컬럼으로 풀러주는 과정을 거쳐야한다.

    #챔피언아이디를 제외하고 딕셔너리를 뽑는다
    a_ls = list(data['teams'])
    #team1
    team1_df = pd.DataFrame()
    for i in range(len(a_ls)):
        try:
            a_ls[i][0].pop('bans',None)
            team1 = pd.DataFrame(list(a_ls[i][0].values()),index = list(a_ls[i][0].keys())).T
            team1_df = team1_df.append(team1)
        except:
            pass
        
    team1_df.index = range(len(team1_df))
    
    #team2
    team2_df = pd.DataFrame()
    for i in range(len(a_ls)):
        try:
            a_ls[i][1].pop('bans',None)
            team2 = pd.DataFrame(list(a_ls[i][1].values()),index = list(a_ls[i][1].keys())).T
            team2_df = team2_df.append(team2)
        except:
            pass
        
    team2_df.index = range(len(team2_df))
    
    #컬럼으로 풀어준 team1과 team2와 duration의 데이터를 합쳐준다.
    data_team = pd.concat([team1_df,team2_df,data[['gameDuration']]],axis=1)

    위의 방법과 동일하게 리스트안에 딕셔너리가 있는 구조인 participants컬럼의 데이터도 풀어주신 다음에 데이터프레임화를 시켜주시면 됩니다.

     

    지금까지 Riot API를 이용하여 리그오브레전드라는 게임의 여러 데이터들을 수집해보았습니다.

    이처럼 내가 관심있어 하는 게임에 대한 데이터를 수집하고보니 굉장히 많은 것을 시도해보고 싶어졌습니다.

    또한 제가 수집한 데이터 이외에 여러분들이 관심 있어할만한 데이터들이 굉장히 많이 있습니다. 그래서 다른 데이터도수집하여 여러분들에게 그리고 다른 사람들에게 도움이 될 수 있는 유용한 정보를 얻어 인사이트를 제공할 수 있는 분석을 해보면 좋을 것 같습니다.

     

     

    다음 포스팅에서는 수집한 17500여개의 데이터를 이용하여 승/패를 예측하는 모델링을 해보는 시간을 갖도록 하겠습니다.

     

     

    그리고 위의 과정없이 바로 분석을 해보실 분들은 아래 제가 구축해놓은 데이터가 있으니 아래의 링크(kaggle dataset)를 타고 다운로드 받아주시면 되겠습니다!

     

    1. 챌린저, 그마, 마스터 랭크게임(바로 분석할 수 있도록 정교하게 데이터 셋 구축한 버전)

     

     

    League Of Legends High elo Ranked Games(2020)

    Classification High elo(Challenger, GrandMaster, Master) Ranked Games Results

    www.kaggle.com

     

    2. 챌린저, 그마, 마스터 경기 시작 후 10분, 15분까지의 게임 데이터(바로 분석할 수 있도록 정교하게 데이터 셋 구축한 버전)

     

    League Of Legends Challenger Rank Game-10min,15min

    Match data from the start of the Challenger Ranked games to 10 and 15 minutes.

    www.kaggle.com

     

    3. 챌린저, 그마, 마스터 랭크게임(모든 경기 데이터가 다 담겨있지만 전처리가 필요한 데이터 셋)

     

    League of Legends(LOL) - Ranked Games 2020

    challenger,grandmaster,master 108,000 game data (Riot, korea, 2020)

    www.kaggle.com

     

     

    4. 챔피언, 아이템에 대한 정보

     

    League of Legends(LOL) CHAMPION and ITEM - 2020

    riot games League of Legends game item and champion information

    www.kaggle.com

     

     


    댓글

Designed by Tistory.