ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 라이엇 api를 활용한 리그오브레전드 데이터 수집
    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번째 탭을 들어가면 얻을 수 있습니다.

    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

     

     


    댓글 31

    • ㅇㅇ 2020.03.02 15:48

      그 혹시
      a_ls = list(data['teams'])
      이부분이 어떤식으로 처리되는지 설명해주실수있을까요 제가 data를 다른 dataframe으로 해보니까 teams가 리스트형태로 나오지않고 앞에 ""가 붙어서 str처리가 되더라구요..ㄷ

      • 초록 아보카도 2020.04.08 18:06 신고

        혹시 해결 하셨나요..? 저도 같은 문제를 겪고있어서요..

      • 사용자 me뇽 2020.04.12 16:06 신고

        헉...그 부분은 아마 두분의 데이터 타입이 string형태로 되어 있어서 그럴 것 같은데

        혹시 api를 통해서 수집하셨나요? 아니면 다른 데이터 csv파일을 받아오셨나요??

        api를 통해서 받아오셨으면 아마 문제가 없을건데, csv파일 형태로 다른 곳에서 받아왔다면 그런 문제가 생길 수 있거든요.

        기존에 teams데이터가 json형태로 되어 있어서 저는 해당 파일을 관리할때 .pickle로 관리하거든요. 만약 필요시 댓글 달아주시면 pickle형태의 최신 데이터 보내드리겠습니다.

      • 초록 아보카도 2020.04.13 16:56 신고

        네네 중간에 csv로 저장하고 불러오는 과정에서 발생했더라고요. 다시 하니까 되네요:)

        참고로 당시 ast.literal_eval을 활용해보긴 했습니다.

        포스팅 잘 보고 있습니다. 많은 도움이 됩니다!

    • 안린이 2020.03.08 03:27

      라이엇 API를 활용한 앱을 개인적으로 만들어보고 싶은 대학생입니다.
      컴퓨터 전공이기는 하지만, 아직 제대로된 앱이나 프로그램을 만들어 본 적이 없어서 막연한 두려움이 있었습니다.
      하지만 본 포스팅을 "나도 해보고 싶다"는 생각도 들고 의욕도 활활 타오르네요!
      포스팅 감사합니다!

      • 사용자 me뇽 2020.03.08 15:13 신고

        헐...너무 감사한 댓글이네요! 분석가로써 최대한 해본 것들인데 아마 저보다 훨씬 완성된 결과물을 만들어 내실 수 있을거에요! 화이팅입니다 ㅎㅎ

    • 코린이 2020.05.03 01:21

      안녕하세요 혹시 api key를 dashboard에 있는걸 사용하면 갱신을 해줘야 하던데 영구적으로 사용하는 방법이 있을까요??

      • 사용자 me뇽 2020.05.03 10:40 신고

        아마 반영구/영구적으로 사용하려면 비즈니스용으로 따로 받아야 하는 것으로 알고 있습니다. 그래서 op.gg나 fow는 그런 식으로 사용하는 것 같습니다.

        그래서 저도 크롤링으로 자동 api갱신을 하려고 봇을 만들어 봤는데 실패해가지고ㅠㅠㅠ 항상 1일마다 수동으로 재갱신을 해주는 방법밖에 없는 것 같습니다!

    • 초보코딩러 2020.05.04 01:35

      날짜별 티어 변동을 알고싶을때는 어떻게 해야할까요? 포스팅 혹시 가능하신 부분일까요?!

      • 사용자 me뇽 2020.05.04 10:07 신고

        날짜별 티어변동 데이터는 특정 유저만 알고 싶으신건가요? 전체 유저들을 대상으로 알고 싶으신건가요?

        특정유저들만을 대상으로 한다고 예를 들면 챌린저, 그랜드마스터, 마스터의 날짜별 티어변동 데이터를 뽑기 위해서는 일별로 데이터 유저들의 티어 데이터를 꾸준히 수집해서 구축하면 될 듯 합니다. 곧 게임 내 시간별 데이터 수집 방법을 포스팅으로 올릴 예정인데 그 때 같이 올려보도록 하겠습니다 ㅎㅎ

    • ds 2020.05.12 02:15

      포스팅 정말 잘 봤습니다.
      이번에 웹 공부하면서 배워볼려고 하는데 이 부분에 대해서 좀 너무 궁금한게 많네요.
      api를 따오는것 까지는 이해가 가는데 Django 라는 파이썬으로 작성된 오픈 소스 웹 애플리케이션으로 사용하는 건가요??
      여러모로 궁금한게 많아 따로 여쭈어 보고싶습니다!!

      • 사용자 me뇽 2020.05.12 13:28 신고

        혹시 django로 어떤것을 사용한다는 것인지 다시 질문해주실 수 있나요? 메일로 연락이 편하시면 tlsalsdyd1995@gmail.com 으로 연락주시면 답변드리겠습니다 ㅎㅎ

    • asd 2020.05.20 16:05

      안녕하세요 포스팅 된 글 잘봤습니다. 코딩실력 부럽네요..
      혹시 한국서버 일별 롤 접속량 데이터도 구축할 수 있을까요??

    • deneb 2020.05.21 01:35

      특정 플레이어의 정글 동선 히트맵이나 동선 표본 같은것을 수집할 수 있나요 ??

      • 사용자 me뇽 2020.05.26 10:06 신고

        네 riot api로 원하는 특정 유저의 summonerId와 accountId만 있다면 Match API의 timeline api를 이용하면 수집 가능하십니다!

        timeline api에서 확인할 수 있는 정보는 시간대별 각 유저의 특정 좌표값(x,y)인데요, 이 좌표값을 리그오브레전드 소환사의 협곡 표준 이미지에 찍어보면 한 게임 내의 해당 유저의 동선을 시각화할 수 있으며 또한 시간별로도 이동한 동선을 시각화할 수 있습니다!

    • 2020.05.29 14:43

      비밀댓글입니다

    • 2020.06.09 19:01

      비밀댓글입니다

    • 2020.06.09 19:02

      matches_info_df2는 어디서 생성한건가요...?

    • 음냐 2020.06.09 20:57

      혹시 matches_info_df2는 matches_info_df를 복사해서 사용하면 되는건가요?? 그리고 a_ls = list(data['teams'])에서 data는 어디서 선언해서 사용하는건가요...ㅠㅠ

      • 사용자 me뇽 2020.06.13 18:54 신고

        아 윗분이랑 비슷한 상황이시네요... 정말 죄송합니다 조만간에 정확히 어떤 데이터고 변수를 사용하는지 수정해서 올리도록 하겠습니다

    • KITE 2020.06.19 16:56

      안녕하세요 저도 롤을 좋아하는 유저로서 아주 흥미로운 포스팅입니다
      혹시 해당 과정을 제 블로그에 따로 정리해도 괜찮을까요?
      물론 출처는 당연히 남기겠습니다!

      그리고 gameid 중복제거 한 이유가 궁금합니다! 유일한 gameid있는 행만 남기고 나머지 행은 모두 제거하신건가요?

    • 2020.07.10 03:38

      안녕하세요 만약 2번의 고유 id를 통하여 소환사 정보 수집에서 403 에러가 나게 된다면
      /lol/league/v4/entries/by-summoner/{encryptedSummonerId}를 사용하면 해결됩니다.
      이전 버전의 api를 사용해서 생기는 문제 같습니다.
      이 글을 보고 따라 해보시다가 오류가 나실 경우에 참고하시기 바랍니다.

      이와 별개로 api를 통해 데이터셋을 받는 과정을 친절하게 알려주셔서 감사합니다. ^^ 천천히 따라 해보고 있습니다.

    • 1234 2020.07.25 16:58

      선생님 안녕하세요!
      혹시 이런식으로 상세하게 검색도 가능한가요?
      한국섭 / 야스오 / 솔로랭크 / 트리플킬이상 /최신날짜
      감사합니다^^

    • replay룰루 2020.07.31 23:53

      선생님, 비밀댓글이 안보여서,, 공개로 말씀해주실 수 있을까요?
      저에게 댓글이 안보이네요;;

      (티스토리 정책을 찾아보니,
      비밀댓글은 블로그주인만 볼 수 있다네요. 저도 이제 알았네요)

Designed by Tistory.