๐Ÿ Python/์ด๋ก 

[aiohttp] API ๋ฐ˜๋ณต๋˜๋Š” ๊ตฌ๋ฌธ์„ ๋น„๋™๊ธฐ ํ˜ธ์ถœ๋กœ ์‹œ๊ฐ„ ๋‹จ์ถ•ํ•˜๊ธฐ

๋ฐ์ดํ„ฐํŒ์Šค 2024. 9. 11. 23:39

RIOT API ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‹ค๊ฐ€ aiohttp ๋น„๋™๊ธฐ ์š”์ฒญ์— ๋Œ€ํ•ด ์•Œ๊ฒŒ ๋๋‹ค.

์ฒ˜์Œ ๋ณด๋Š” ํ•จ์ˆ˜์—ฌ์„œ ์ •๋ฆฌ๋ฅผ ํ•˜๊ณ ์ž ๊ธ€์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋๋‹ค.

 

๋ชฉ์ฐจ

โœ…๋ชฉ์ : matchID ๋ฆฌ์ŠคํŠธ๋ฅผ ํ™œ์šฉํ•ด์„œ url 205*20=4100 ๊ฐœ์˜ get ์š”์ฒญ์„ ํ•˜์ž

โœ…ํ•ด๊ฒฐ๋ฐฉ์•ˆ 1 : for loop

โœ…ํ•ด๊ฒฐ๋ฐฉ์•ˆ 2:  aiohttp

โœ… async, await,์ฝ”๋ฃจํ‹ด ๋ž€?

 

 

ํ•ด๊ฒฐ๋ฐฉ์•ˆ 1 : for loop

api_key = MY_API
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
}

match_user_data=[]

for player in summoner_matchID_list:
    puuid=player['puuid']
    match_ids=player['match_ids']
    for match_id in match_ids:
        url = f"https://asia.api.riotgames.com/lol/match/v5/matches/{match_id}"
        rp3=requests.get(url,headers=header)

        if rp3.status_code==429:
            print("Rate limit exceeded. Sleeping for 60 seconds")
            time.sleep(60)
            rp3=requests.get(url,headers=header)
        if rp3.status_code==200:
            match_data=rp3.json()
            match_user_data.append({'puuid': puuid, 'match_id': match_id, 'match_data':match_data})

    # ๋”œ๋ ˆ์ด ์ถ”๊ฐ€
    time.sleep(1)  # ๊ฐ ์š”์ฒญ ์‚ฌ์ด์— 1์ดˆ์˜ ๋”œ๋ ˆ์ด๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ Riot API์˜ Rate Limit์— ๊ฑธ๋ฆฌ์ง€ ์•Š๋„๋ก

 

for ๋ฌธ์„ ๋Œ๋ฉฐ ํ•˜๋‚˜์”ฉ GET ์š”์ฒญ์„ ๋ณด๋‚ด๋Š”๊ฑด ๊ฐ€์žฅ ์‰ฝ๊ฒŒ ์ƒ๊ฐํ•  ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

ํ•˜์ง€๋งŒ ํ•œ ๋ฒˆ์— ํ•œ URL ๋ฐ–์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด์ฒ™ ๋น„ํšจ์œจ์ ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋ฌด๋ ค 40๋ถ„์ด ๋„˜๋Š” ์‹œ๊ฐ„ ๋™์•ˆ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ ๋์Œ์—๋„ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋‹ค.

 

ํ•ด๊ฒฐ๋ฐฉ์•ˆ 2 :  AIOHTTP

import aiohttp # asyncio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTTP ์š”์ฒญ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
import asyncio # Python 3.3๋ถ€ํ„ฐ ์†Œ๊ฐœ๋œ ๋น„๋™๊ธฐ ์ง€์› ๋ชจ๋“ˆ
import time

# ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
async def fetch_match_data(session, puuid, match_id, header): 
    # Riot API์—์„œ ๊ฐ match_id์— ๋Œ€ํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• 
    # session : aiohttp.ClientSession()์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ๋œ HTTP ์„ธ์…˜ ๊ฐ์ฒด. ๋น„๋™๊ธฐ์ ์œผ๋กœ HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ํ•„์š”
    url = f"https://asia.api.riotgames.com/lol/match/v5/matches/{match_id}" 
    async with session.get(url, headers=header) as response:
        # session์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ๊ฐœ์˜ matchID๋ฅผ ๋™์‹œ์— ์ž…๋ ฅํ•˜์—ฌ url ์ฃผ์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ
        if response.status == 429:
            # Rate limit์— ๊ฑธ๋ฆฌ๋ฉด (status == 429), Retry-After ํ—ค๋” ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ 60์ดˆ ๋งŒํผ ๋Œ€๊ธฐํ•œ ํ›„ ์žฌ์‹œ๋„
            retry_after = int(response.headers.get('Retry-After', 60))  # Retry-After ํ—ค๋” ํ™•์ธ
            print(f"Rate limit exceeded. Sleeping for {retry_after} seconds.")
            await asyncio.sleep(retry_after)  # ๋น„๋™๊ธฐ ๋Œ€๊ธฐ
            # 60์ดˆ๋งŒํผ ๋Œ€๊ธฐ์ค‘์ธ ์ƒํƒœ
            async with session.get(url, headers=header) as retry_response:
                if retry_response.status == 200:
                    # 60์ดˆ๋งŒํผ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„
                    return await retry_response.json(), puuid, match_id
        elif response.status == 200:
            return await response.json(), puuid, match_id
            # ์‘๋‹ต์ด ์„ฑ๊ณต์ ์ผ ๊ฒฝ์šฐ (status == 200), ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ json ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜
        else:
            print(f"Error: {response.status}")
            return None, puuid, match_id
            # ์˜ค๋ฅ˜ ์‘๋‹ต์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ ์ƒํƒœ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  None์„ ๋ฐ˜ํ™˜

# ๋น„๋™๊ธฐ ๋ฉ”์ธ ํ•จ์ˆ˜
async def fetch_all_matches(summoner_matchID_list, header):#์—ฌ๋Ÿฌ ํ”Œ๋ ˆ์ด์–ด์˜ ์—ฌ๋Ÿฌ match_id์— ๋Œ€ํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ API ์š”์ฒญ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌ
    match_user_data = []
    async with aiohttp.ClientSession() as session:
        tasks = []
        # ๊ฐ ํ”Œ๋ ˆ์ด์–ด์˜ puuid์™€ ํ•ด๋‹น match_ids์— ๋Œ€ํ•ด fetch_all_data ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ž‘์—…(task)์„ ์ƒ์„ฑ
        for player in summoner_matchID_list:
            puuid = player['puuid']
            match_ids = player['match_ids']
            for match_id in match_ids:
                task = asyncio.create_task(fetch_match_data(session, puuid, match_id, header)) 
                # asyncio.create_task()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ƒ์„ฑํ•˜๊ณ , ์ด ์ž‘์—…์„ ๋ชจ๋‘ ๋ชจ์•„์„œ asyncio.gather()๋กœ ๋™์‹œ์— ์‹คํ–‰
                tasks.append(task)
                await asyncio.sleep(1)  # ๋น„๋™๊ธฐ ๋Œ€๊ธฐ (1์ดˆ)
        responses = await asyncio.gather(*tasks)
        
        # ์‘๋‹ต ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
        for match_data, puuid, match_id in responses:
            if match_data:
                match_user_data.append({'puuid': puuid, 'match_id': match_id, 'match_data': match_data})

    return match_user_data

 

2์ดˆ๋งŒ์— ์‹คํ–‰๋˜์—ˆ๊ณ  API๋„ ์ œ๋Œ€๋กœ ํ˜ธ์ถœ๋˜์—ˆ๋‹ค.

์„ฑ๋Šฅ ์ฉ๋‹ค.. ์ฝ”๋“œ์— ๋Œ€ํ•œ ํ•ด์„๋„ ๋‹ฌ์•„ ๋†“์•˜๋‹ค.

 

 

 

async, await, coroutine ์ด๋ž€?

async def : ์ฝ”๋ฃจํ‹ด์„ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•œ ํ‚ค์›Œ๋“œ

coroutine : ์ง„์ž…์ ์ด ์—ฌ๋Ÿฌ๊ฐœ์ธ ํ•จ์ˆ˜, ๋ณดํ†ต ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์ฒ˜์Œ ํ˜ธ์ถœ๋œ ์‹œ์ ์ด ์œ ์ผํ•œ ์ง„์ž…์ ์ž„

asyncio.wait : ์ด task๋“ค์˜ list์ธ tasks๊ฐ€ ์ „๋ถ€ ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.