๐Ÿ“ฆ๋ถ„์„ ํ”„๋กœ์ ํŠธ/๐ŸŽฎ RIOT API ๋ถ„์„ ๋ฐ ์›น์„œ๋น„์Šค ๊ฐœ๋ฐœ

๐ŸŽฎRIOT API ๋ถ„์„ ํ”„๋กœ์ ํŠธ(8) - GrandMaster ํ‹ฐ์–ด์˜ DB๊ตฌ์ถ•

๋ฐ์ดํ„ฐํŒ์Šค 2024. 9. 22. 12:32

โœ… ๋ฐฐ๊ฒฝ : API๋ฅผ ํ™œ์šฉํ•ด DB ๊ตฌ์ถ• ๋ฐ ์„œ๋น„์Šค ์šด์˜ ๊ฒฝํ—˜

โœ… ๋ชฉ์  : RIOT LOL API๋ฅผ ํ™œ์šฉํ•ด '๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ' ๊ตฌ๊ฐ„์˜ ์‹ค์ œ ํ‹ฐ์–ด๋ฅผ ํ™•์ธ ๊ฐ€๋Šฅํ•œ ์›น ์„œ๋น„์Šค ๋งŒ๋“ค๊ธฐ

โœ… ๊ธฐ๊ฐ„ : 2024.08.25 ~ 2024.09.22 (์˜ˆ์ •)

โœ… ์ธ์› : 1๋ช… (2๋ช…์œผ๋กœ ์‹œ์ž‘ํ–ˆ์œผ๋‚˜ ํ”„๋กœ์ ํŠธ์—์„œ ํ•˜์ฐจํ•˜์—ฌ ํ˜ผ์ž ๋‹ค ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค)

โœ… ์—ญํ•  : python์„ ํ™œ์šฉํ•ด api ํ˜ธ์ถœ ๋ฐ ์œ ์ €๋“ค์˜ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด DB ์ƒ์„ฑ,

                       SQL ์ฟผ๋ฆฌ๋ฌธ์„ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ ์ถ”์ถœ ๋ฐ ๊ฐ€๊ณต,

                       Tableau๋ฅผ ํ™œ์šฉํ•ด DB ์ง€ํ‘œ ๋Œ€์‹œ๋ณด๋“œ ์ƒ์„ฑ,

                       html, flask๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์›นํŽ˜์ด์ง€ ๊ตฌํ˜„


LEAGUE - VR - GRANDMASTER - queue๋ฅผ ํ†ตํ•ด summonerID ์ถ”์ถœ

import requests

def get_grandmaster_summoner_ids(api_key):
    request_header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
        "Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
        "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
        "Origin": "https://developer.riotgames.com",
        "X-Riot-Token": api_key  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ์ €์žฅ๋œ API ํ‚ค ์‚ฌ์šฉ
    }

    base_url = "https://kr.api.riotgames.com/lol/league/v4/grandmasterleagues/by-queue/RANKED_SOLO_5x5"
    
    # ๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ ํ”Œ๋ ˆ์ด์–ด ๋ฐ์ดํ„ฐ๋ฅผ Riot API์—์„œ ๊ฐ€์ ธ์˜ด
    response = requests.get(base_url, headers=request_header)
    
    if response.status_code == 200:
        data = response.json()
        summoner_ids = [entry['summonerId'] for entry in data['entries']]
        return summoner_ids
    else:
        return {"error": f"๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ์ฝ”๋“œ: {response.status_code}"}

# API ํ‚ค ์„ค์ •
api_key = "my_api"

# ํ•จ์ˆ˜ ํ˜ธ์ถœ
grandmaster_summoners = get_grandmaster_summoner_ids(api_key)

# ๊ฒฐ๊ณผ ์ถœ๋ ฅ
print(len(grandmaster_summoners))

 

๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ ํ‹ฐ์–ด๋Š” ๋‹ค๋ฅธ ํ‹ฐ์–ด๋“ค๊ณผ ๋‹ฌ๋ฆฌ tier, division ๊ฐ™์€ ์ •๋ณด๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ ๋Œ€์‹  Riot API์—์„œ๋Š” ๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ์™€ ๋งˆ์Šคํ„ฐ ํ‹ฐ์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ๊ฐ ์ „์šฉ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์ฝ”๋“œ์—์„œ๋Š” ๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์•„๋ž˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ๋‚˜ ๋งˆ์Šคํ„ฐ ํ‹ฐ์–ด์—์„œ๋Š” ์ผ๋ฐ˜์ ์ธ ํ‹ฐ์–ด/๋””๋น„์ „ ์‹œ์Šคํ…œ๊ณผ ๋‹ค๋ฅด๊ฒŒ ํŽ˜์ด์ง•๋„ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

 

 

SUMMONER - V4 ์—์„œ PUUID ์ถ”์ถœ

import aiohttp
import asyncio
from asyncio_throttle import Throttler  # Throttler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ API ์š”์ฒญ ์ˆ˜๋ฅผ ์ œ์–ด

# ๋น„๋™๊ธฐ์ ์œผ๋กœ ์†Œํ™˜์‚ฌ PUUID๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
async def fetch_summoner_puuid(session, summoner_id, header, throttler):
    url = f"https://kr.api.riotgames.com/lol/summoner/v4/summoners/{summoner_id}"
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
        "Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
        "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
        "Origin": "https://developer.riotgames.com",
        "X-Riot-Token": "my_api"
    }
    
    async with throttler:  # Throttler๋กœ ์š”์ฒญ ์†๋„๋ฅผ ์ œ์–ด
        async with session.get(url, headers=header) as response:
            if response.status == 429:  # Rate limit ์ฒ˜๋ฆฌ
                retry_after = int(response.headers.get('Retry-After', 60))
                print(f"Rate limit exceeded. Sleeping for {retry_after} seconds for {summoner_id}.")
                await asyncio.sleep(retry_after)
                return await fetch_summoner_puuid(session, summoner_id, header, throttler)  # ์žฌ๊ท€ ํ˜ธ์ถœ๋กœ ์žฌ์‹œ๋„
            elif response.status == 200:
                summoner_data = await response.json()
                return summoner_data.get('puuid')
            else:
                print(f"Error: {response.status} for summoner ID {summoner_id}")
                return None

# ๋น„๋™๊ธฐ์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์†Œํ™˜์‚ฌ์˜ PUUID๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
async def fetch_all_summoner_puuids(api_key, grandmaster_summoners):
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
        "Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
        "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
        "Origin": "https://developer.riotgames.com",
        "X-Riot-Token": "my_api"
    }

    summoner_puuid_list = []
    throttler = Throttler(rate_limit=20, period=1)  # ์ดˆ๋‹น 20๊ฐœ์˜ ์š”์ฒญ๋งŒ ํ—ˆ์šฉ, riot api๊ฐ€ ์ดˆ๋‹น 20๊ฐœ๊ฐ€ ํ•œ๊ณ„์ž„

    async with aiohttp.ClientSession() as session:
        tasks = []
        for summoner_id in grandmaster_summoners:
            task = fetch_summoner_puuid(session, summoner_id, header, throttler)  # ๋น„๋™๊ธฐ ์ž‘์—… ์ƒ์„ฑ
            tasks.append(task)
        
        # ๋ชจ๋“  ๋น„๋™๊ธฐ ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰
        responses = await asyncio.gather(*tasks)
        
        for puuid in responses:
            if puuid:
                summoner_puuid_list.append(puuid)

    return summoner_puuid_list

# ๋น„๋™๊ธฐ ์‹คํ–‰์„ ์œ„ํ•œ ๋ฉ”์ธ ํ•จ์ˆ˜
async def main():
    api_key = "MY_API"  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ
    
    # ์—ฌ๋Ÿฌ ์†Œํ™˜์‚ฌ์˜ PUUID๋ฅผ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
    summoner_puuid_list = await fetch_all_summoner_puuids(api_key, grandmaster_summoners)
    
    # ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
    return summoner_puuid_list

# ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์‹คํ–‰
summoner_puuid_list = await main()

# ๊ฒฐ๊ณผ ์ถœ๋ ฅ
print(f"Summoner PUUID List: {summoner_puuid_list}")

 

12๋ถ„ ์ •๋„์˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ ค์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”์ถœ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ SQL DB์— ์ €์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค MATCHID ์ถ”์ถœํ• ๋•Œ ์ €์žฅํ•˜๋ฉด ๋˜์ง€๋งŒ ๋ฐ์ดํ„ฐ ๋‚ ์•„๊ฐˆ๊นŒ๋ด ๋ฏธ๋ฆฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค!

 

 

 

MYSQL DB์— PUUID ์ €์žฅ ๋ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ 

import aiomysql
import asyncio


# MySQL์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ํ•จ์ˆ˜
async def save_puuids_to_mysql(summoner_puuid_list, db_config):
    # MySQL์— ์—ฐ๊ฒฐ
    conn = await aiomysql.connect(
        host=db_config["host"],
        port=db_config["port"],
        user=db_config["user"],
        password=db_config["password"],
        db=db_config["db"],
        charset='utf8mb4'
    )
    
    async with conn.cursor() as cursor:
        for puuid in summoner_puuid_list:
            # ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
            query = """
            INSERT INTO summoner_puuid (puuid)
            VALUES (%s)
            ON DUPLICATE KEY UPDATE puuid=VALUES(puuid)
            """
            await cursor.execute(query, (puuid,))
        
        # ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ปค๋ฐ‹
        await conn.commit()

    # MySQL ์—ฐ๊ฒฐ ์ข…๋ฃŒ
    conn.close()

# ๋น„๋™๊ธฐ ์‹คํ–‰์„ ์œ„ํ•œ ๋ฉ”์ธ ํ•จ์ˆ˜
async def main():
    # MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •๋ณด ์„ค์ •
    db_config = {
        "host": "localhost",       # MySQL ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ
        "port": 3306,              # MySQL ํฌํŠธ
        "user": "root",            # MySQL ์‚ฌ์šฉ์ž ์ด๋ฆ„
        "password": "๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ",    # MySQL ๋น„๋ฐ€๋ฒˆํ˜ธ
        "db": "riot_game_db"       # ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„
    }

    # PUUID ๋ฐ์ดํ„ฐ๋ฅผ MySQL์— ์ €์žฅ
    await save_puuids_to_mysql(summoner_puuid_list, db_config)

# ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์‹คํ–‰
await main()

 

์ €์žฅ์€ ์ž˜ ๋˜์—ˆ๊ณ  ๋‹ค์‹œ ์ œ๋Œ€๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

 

 

import mysql.connector

# MySQL ์„œ๋ฒ„์— ์—ฐ๊ฒฐ
connection = mysql.connector.connect(
    host="localhost",         # MySQL ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ ์ด๋ฆ„ (๋ณดํ†ต ๋กœ์ปฌ์ผ ๊ฒฝ์šฐ localhost)
    user="root",      # MySQL ์‚ฌ์šฉ์ž ์ด๋ฆ„
    password="0224",  # MySQL ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
    database="riot_game_db"    # ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„
)

# ์—ฐ๊ฒฐ์ด ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ํ™•์ธ
if connection.is_connected():
    print("MySQL ์—ฐ๊ฒฐ ์„ฑ๊ณต")
else:
    print("MySQL ์—ฐ๊ฒฐ ์‹คํŒจ")

MySQL ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜์—ฌ DB๋ฅผ ์ œ๋Œ€๋กœ ๊ฐ€์ง€๊ณ  ์™”์Šต๋‹ˆ๋‹ค.

 

 

import pandas as pd

# ์ฟผ๋ฆฌ ์‹คํ–‰ ํ›„ DataFrame์œผ๋กœ ๋ณ€ํ™˜
query = "SELECT * FROM summoner_puuid"  # ์‚ฌ์šฉํ•  ์ฟผ๋ฆฌ
df = pd.read_sql(query, connection)

# DataFrame ์ถœ๋ ฅ
print(df)

summoner_puuid_list = df['puuid'].tolist()

 

์ œ๊ฐ€ ์“ธ ํ…Œ์ด๋ธ”๋งŒ์„ SQL์—์„œ ๊ฐ€์ ธ์™€์„œ ํ•ด๋‹น ํ…Œ์ด๋ธ”์„ DFํ™” ํ•˜๊ณ , ํ•„์š”ํ•œ ์นผ๋Ÿผ๋งŒ์„ ๋ฆฌ์ŠคํŠธ ํ˜•์‹์œผ๋กœ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

 

MATCH-V4 ์—์„œ MATCHID ๊ฐ€์ ธ์˜ค๊ธฐ

import aiohttp
import asyncio
from asyncio_throttle import Throttler  # asyncio_throttle ๊ฐ€์ ธ์˜ค๊ธฐ

# ๋น„๋™๊ธฐ์ ์œผ๋กœ ์†Œํ™˜์‚ฌ matchID๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
async def fetch_summoner_matchID(session, puuid, header, throttler):
    url = f"https://asia.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids?type=ranked&start=0&count=1"
    
    async with throttler:  # ์š”์ฒญ ์†๋„๋ฅผ ์ œํ•œํ•˜๋Š” Throttler ์‚ฌ์šฉ
        async with session.get(url, headers=header) as response:
            if response.status == 429:  # Rate limit ์ฒ˜๋ฆฌ
                retry_after = int(response.headers.get('Retry-After', 60))
                print(f"Rate limit exceeded for {puuid}. Sleeping for {retry_after} seconds.")
                await asyncio.sleep(retry_after)
                return await fetch_summoner_matchID(session, puuid, header, throttler)  # ์žฌ์‹œ๋„
            
            if response.status == 200:
                summoner_match_data = await response.json()
                return {'puuid': puuid, 'match_ids': summoner_match_data}
            else:
                print(f"Error: {response.status} for PUUID {puuid}")
                return {'puuid': puuid, 'match_ids': None}

# ๋น„๋™๊ธฐ์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์†Œํ™˜์‚ฌ์˜ matchID๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
async def fetch_all_summoner_matchIDs(api_key, summoner_puuid_list):
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
        "Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
        "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
        "Origin": "https://developer.riotgames.com",
        "X-Riot-Token": api_key
    }

    summoner_matchID_list = []
    throttler = Throttler(rate_limit=20, period=1)  # ์ดˆ๋‹น ์ตœ๋Œ€ 20๊ฐœ์˜ ์š”์ฒญ๋งŒ ํ—ˆ์šฉ

    async with aiohttp.ClientSession() as session:
        tasks = []
        for puuid in summoner_puuid_list:
            task = fetch_summoner_matchID(session, puuid, header, throttler)  # ๋น„๋™๊ธฐ ์ž‘์—… ์ƒ์„ฑ
            tasks.append(task)
        
        # ๋ชจ๋“  ๋น„๋™๊ธฐ ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰
        responses = await asyncio.gather(*tasks)
        
        for response in responses:
            if response and response.get('match_ids') is not None:
                summoner_matchID_list.append(response)

    return summoner_matchID_list

# ๋น„๋™๊ธฐ ์‹คํ–‰์„ ์œ„ํ•œ ๋ฉ”์ธ ํ•จ์ˆ˜
async def main():
    api_key = "my_api"  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ
    
    # ์—ฌ๋Ÿฌ ์†Œํ™˜์‚ฌ์˜ matchID๋ฅผ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
    summoner_matchID_list = await fetch_all_summoner_matchIDs(api_key, summoner_puuid_list)
    
    # ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
    return summoner_matchID_list

# ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์‹คํ–‰
summoner_matchID_list = await main()

# ๊ฒฐ๊ณผ ์ถœ๋ ฅ
print(f"Summoner MatchID List: {summoner_matchID_list}")

 

์ตœ๊ทผ ํ•œํŒ๋งŒ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต์‹œ๋‹ค.

MATCHID๋ฅผ ํ™œ์šฉํ•ด ๊ฒŒ์ž„์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๋‹ค์Œ์— PUUID์— ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” ์ •๋ณด๋งŒ์„ ๋‚จ๊ฒจ์„œ ๋ฝ‘๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์„  ์œ ์ € DB๋ฅผ ๊ตฌ์ถ•ํ•ด์•ผ ํ•˜๋‹ˆ๊นŒ์š”. 

 

 

 

 

MYSQL DB์— puuid, matchID ์ €์žฅ

import mysql.connector

# MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ
connection = mysql.connector.connect(
    host="localhost",        # MySQL ์„œ๋ฒ„ ์ฃผ์†Œ
    user="root",    # MySQL ์‚ฌ์šฉ์ž๋ช…
    password="pw", # MySQL ๋น„๋ฐ€๋ฒˆํ˜ธ
    database="riot_game_db"  # ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„
)

# ์ปค์„œ ์ƒ์„ฑ
cursor = connection.cursor()

# puuid์™€ match_id ๋ฐ์ดํ„ฐ๋ฅผ ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…ํ•˜๋Š” ํ•จ์ˆ˜
def insert_match_id(puuid, match_id):
    sql = """
    INSERT INTO summoner_list (puuid, match_id)
    VALUES (%s, %s)
    """
    cursor.execute(sql, (puuid, match_id))


# puuid์™€ ๊ฐ๊ฐ์˜ match_id๋ฅผ ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…
for entry in summoner_matchID_list:
    puuid = entry['puuid']
    match_ids = entry['match_ids']
    
    for match_id in match_ids:
        insert_match_id(puuid, match_id)

# ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ปค๋ฐ‹
connection.commit()

# ์ปค์„œ์™€ ์—ฐ๊ฒฐ ์ข…๋ฃŒ
cursor.close()
connection.close()

์ œ๋Œ€๋กœ ์ €์žฅ๋์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

ํ•ด๋‹น matchID๋ฅผ ๊ฐ€์ง€๊ณ  ๊ฒŒ์ž„์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ(puuid,summonerName)

import aiohttp
import asyncio
import mysql.connector
from asyncio_throttle import Throttler

# ๋น„๋™๊ธฐ์ ์œผ๋กœ match ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
async def fetch_match_data(session, match_id, api_key, throttler):
    url = f"https://asia.api.riotgames.com/lol/match/v5/matches/{match_id}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
        "Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
        "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
        "Origin": "https://developer.riotgames.com",
        "X-Riot-Token": api_key
    }
    
    async with throttler:  # ์š”์ฒญ ์†๋„ ์ œํ•œ
        async with session.get(url, headers=headers) as response:
            if response.status == 429:  # Rate limit ์ฒ˜๋ฆฌ
                retry_after = int(response.headers.get('Retry-After', 60))  # Retry-After ํ—ค๋” ํ™•์ธ
                print(f"Rate limit exceeded for match {match_id}. Sleeping for {retry_after} seconds.")
                await asyncio.sleep(retry_after)  # ๋น„๋™๊ธฐ ๋Œ€๊ธฐ
                return await fetch_match_data(session, match_id, api_key, throttler)  # ๋‹ค์‹œ ์‹œ๋„
            elif response.status == 200:
                match_data = await response.json()  # JSON ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
                queue_id = match_data.get('info', {}).get('queueId', None)
                if queue_id == 420:  # Solo Rank ๋ชจ๋“œ (Queue ID 420)๋งŒ ๊ฐ€์ ธ์˜ด
                    return match_data
                else:
                    print(f"Skipping match {match_id} because queueId is not 420")
                    return None
            else:
                print(f"Error: {response.status} for match ID {match_id}")
                return None

async def fetch_all_matches(match_ids, api_key):
    throttler = Throttler(rate_limit=20, period=1)  # ์ดˆ๋‹น 20๊ฐœ์˜ ์š”์ฒญ์œผ๋กœ ์ œํ•œ
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_match_data(session, match_id, api_key, throttler) for match_id in match_ids]
        responses = await asyncio.gather(*tasks)
        return [response for response in responses if response is not None]

# MYSQL ์—ฐ๊ฒฐ ์„ค์ •
def create_db_connection():
    connection = mysql.connector.connect(
        host="localhost",         # MySQL ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ ์ด๋ฆ„
        user="root",              # MySQL ์‚ฌ์šฉ์ž ์ด๋ฆ„
        password="0224",          # MySQL ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
        database="riot_game_db"  # ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„
    )
    return connection

# MYSQL์— match_id, puuid, summoner_name ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
def insert_match_data(connection, match_data):
    cursor = connection.cursor()
    inser_query = """
    INSERT INTO matches (match_id, puuid, summoner_name)
    VALUES (%s, %s, %s)
    """
    
    for match in match_data:
        match_id = match.get('metadata', {}).get('matchId', 'Unknown Match ID')
        for participant in match.get('info', {}).get('participants', []):
            data = (
                match_id,
                participant.get('puuid'),
                participant.get('summonerName')
            )
            try:
                cursor.execute(inser_query, data)
            except mysql.connector.Error as err:
                print(f"Error: {err}")
    
    connection.commit()
    cursor.close()

# ๋น„๋™๊ธฐ ๋ฉ”์ธ ํ•จ์ˆ˜
async def main():
    api_key = "my_api"  # Riot Games API ํ‚ค
    
    # ์—ฌ๋Ÿฌ ๋งค์น˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
    match_data_list = await fetch_all_matches(match_ids, api_key)
    
    # MySQL ์—ฐ๊ฒฐ ๋ฐ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
    connection = create_db_connection()
    insert_match_data(connection, match_data_list)
    connection.close()

# asyncio ์‹คํ–‰
await main()

์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ SQL์—์„œ ๊ฐœ์ˆ˜๋ฅผ ์„ธ์–ด๋ด…์‹œ๋‹ค.

 

 

4910๊ฐœ๋กœ ์ด๋ ‡๊ฒŒ ๋งŽ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค...

๊ทธ ์ด์œ ๋Š” ๊ทธ๋งˆ ํ‹ฐ์–ด์˜ puuid ์ถ”์ถœ > ์ตœ๊ทผ ํ•œํŒ matchID ์ถ”์ถœ > matchID 1๊ฐœ์—๋Š” 10๋ช…์˜ ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๊ธฐ์— ์ฐธ์—ฌํ•œ ์‚ฌ๋žŒ์˜ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์ œ๊ณต๋จ > ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๊ทธ๋งˆ ํ‹ฐ์–ด puuid ์ถ”์ถœํ•œ ๊ฒƒ๊ณผ ์ผ์น˜๋˜๋Š” ๊ฒƒ๋งŒ์„ ๋‚จ๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค

 

 

GrandMaster ํ‹ฐ์–ด์— ํ•ด๋‹น๋˜๋Š” ์œ ์ €๋งŒ ์ถ”์ถœํ•˜๊ธฐ

CREATE TABLE GM_users AS
SELECT m.match_id, m.puuid, m.summoner_name
FROM matches m
JOIN summoner_list s ON m.puuid = s.puuid;

 

๊ทธ๋ž˜๋„ ์—ฌ์ „ํžˆ 2611๊ฐœ์ž…๋‹ˆ๋‹ค. ์ผ๋‹จ ๊ทธ๋žœ๋“œ ๋งˆ์Šคํ„ฐ ํ‹ฐ์–ด๊ฐ€ ์•„๋‹Œ๊ฒฝ์šฐ๋Š” ๋‹ค ์ œ์™ธํ•˜์˜€์Šต๋‹ˆ๋‹ค.  

์ค‘๋ณต๋˜๋Š” ํ•ญ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค. 

 

 

#์ค‘๋ณตํ•ญ ์žˆ๋Š”์ง€ ํ™•์ธ
SELECT puuid, COUNT(*)
FROM GM_users
GROUP BY puuid
HAVING COUNT(*) > 1;

SELECT COUNT(*)
FROM (
    SELECT puuid
    FROM GM_users
    GROUP BY puuid
    HAVING COUNT(*) > 1
) AS duplicate_puuid;

 

๋™์ผํ•œ puuid๊ฐ€ ์—ฌ๋ŸฌํŒ์— ์ฐธ์—ฌํ•˜๋ฉด์„œ matchID๋งŒ ๋‹ค๋ฅด๊ณ  ์ค‘๋ณต๋œ puuid๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

SELECT *
FROM GM_users
WHERE puuid IN (
    SELECT puuid
    FROM GM_users
    GROUP BY puuid
    HAVING COUNT(DISTINCT summoner_name) > 1
);

puuid๊ฐ€ ์ค‘๋ณต๋˜๊ณ  summonerName์ด ๋‹ค๋ฅธ ๋ชจ๋“  ํ–‰์ด ์žˆ๋Š”์ง€ ์ฐพ์•„๋ดค๋Š”๋ฐ ์—†๋„ค์š”.

๋‹‰๋ณ€ํ•œ์‚ฌ๋žŒ์ด ์—†๋‚˜ ๋ด…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ผ๋‹จ ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด์„œ ์ค‘๋ณต๊ฐ’์„ ์—†์• ์ค์‹œ๋‹ค. 

 

 

CREATE TABLE GM_users2 AS
SELECT puuid, summoner_name
FROM GM_users;

puuid ๋ฆฌ์ŠคํŠธ ๊ฐ’์€ 700๊ฐœ์ธ๋ฐ ์ œ๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฒƒ์ธ 648๊ฐœ ๋ฐ–์— ์•ˆ๋˜๋„ค์š”.

์–ด๋–ค ๊ฐ’๋“ค์ด ๋ˆ„๋ฝ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค. duplicated_GM_users๋ผ๋Š” ํ…Œ์ด๋ธ”์„ ๋˜ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

duplicated_GM_users ํ…Œ์ด๋ธ”์— ์žˆ๋Š” puuid์™€ summoner_list ํ…Œ์ด๋ธ”์— ์žˆ๋Š” puuid๋ฅผ ๋น„๊ตํ•˜์—ฌ,

duplicated_GM_users ํ…Œ์ด๋ธ”์— ์—†๋Š” puuid ๊ฐ’์„ ํ™•์ธํ•˜๋ ค๋ฉด LEFT JOIN ๋˜๋Š” NOT IN์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

SELECT puuid
FROM summoner_list
WHERE puuid NOT IN (
    SELECT puuid
    FROM duplicated_GM_users
);

SELECT COUNT(*) AS missing_puuid_count
FROM summoner_list
WHERE puuid NOT IN (
    SELECT puuid
    FROM duplicated_GM_users
);

์—ญ์‹œ 52๊ฐœ๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ๋„ค์š”.

 

์›์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€ puuid๊ฐ€ ๊ฒŒ์ž„ ์ •๋ณด ํ…Œ์ด๋ธ”์— ์กด์žฌํ•˜์ง€ ์•Š์•„์„œ ๋ˆ„๋ฝ๋œ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

matchID์—์„œ ๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ ์œ ์ €์˜ ๊ฒŒ์ž„ ์ •๋ณด๊ฐ€ ์ผ๋ถ€ ๋ˆ„๋ฝ๋œ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ matchID๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •์—์„œ ํ•„ํ„ฐ๋ง์ด ์ž˜๋ชป๋˜์—ˆ๊ฑฐ๋‚˜ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ๋‹ค๋ฉด,

700๊ฐœ์˜ puuid๋ฅผ ์™„์ „ํžˆ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ–ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ˆ„๋ฝ๋œ 52๋ช…์— ๋Œ€ํ•ด์„œ๋Š” ๊ฒ€์ƒ‰ํ–ˆ์„ ์‹œ Summoner not found๋กœ ํ‘œํ˜„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜์—ฌ puuid ์™€ summoner_name์œผ๋กœ ์กฐํ•ฉ๋œ ๊ทธ๋žœ๋“œ๋งˆ์Šคํ„ฐ ์œ ์ € 648๋ช…์˜ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ์œ ์ €๊ฐ€ ์ž์‹ ์˜ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ–ˆ์„ ์‹œ, ์ œ๊ฐ€ ๋งŒ๋“  DB์—์„œ puuid๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒŒ์ž„ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!