Networks/Project

SK networks AI Camp - Toy Project 전처리 코드 리뷰

코딩하는 Español되기 2024. 8. 11. 19:00

경기 상세 내용을 Json 파일로 저장하여 크롤링을 완료하였습니다.

2024.08.10 - [Networks/Project] - SK networks AI Camp - Toy Project 크롤링 코드 리뷰

 

SK networks AI Camp - Toy Project 크롤링 코드 리뷰

현재 코드를 짜서 화면에 구현하는 것까지 완료하였습니다. 코드를 리뷰하면서 지금까지 진행한 걸 다시 적어보면서 코드를 다시 한번 공부해 보는 시간을 가지겠습니다.(* 크롤링하다가 오류

joowon582.tistory.com

이 자료를 가공하여 쓰기 편하도록 전처리하여 DB에 넣는 코드를 작성하였습니다.

데이터를 2023년 1월부터 2024년 7월까지 받아서 저장하였고 Json에는 아래의 사진 형태로 저장되어 있습니다.

 

진행하기 전 디자인

 

○ 날짜+ 시간을 기준으로 나눠서 각 세트별로 담기

○ Json 파일에서 데이터 형태 파악

    ● list[0]

       - 연 월 일 시간 \n Live \n Channel \n 1st ~ 10th(등수) \n 팀명1 \n 스코어1 \n 스코어2 \n 팀명2 \n 등수
    ● list[1]
       1.팀명1 \n 승패(Victory/Defeat) \n 진형(Blue Side/Red Side) 
       2. \n Champion \n Player \n KDA \n Damage \n Sight \n CS \n Items \n
       3. 레벨(숫자) \n 팀명과 닉네임 \n 킬뎃(n / n / n) \n KDA \n 데미지(XX,XXX) \n sight(n) \n cs(n) \n 분당(n.f)
       4. 3번 5번 후 (한 경기 당 출전 선수 5명)

       5. 팀명2 \n 승패(Victory/Defeat) \n 진형(Blue Side/Red Side) 
       6. 2번과 동일

       7. 팀2의 내용(3번과 형태가 같음) 5번
    ● list[2] : 구분선( '=' * 50 )

 

○ 필요한 데이터

    ● 날짜

    ● 팀별 승/패

    ● 경기 상세 내용

    ● 시즌 정보(날짜별 저장할 때 지정)

코드 설계

○ Json 파일 로드 및 변수 선언

    ● 게임 정보에 사용되는 변수

        - lines : 필요 없는 데이터 전처리 후 데이터를 담아 놓은 리스트

        - date : 경기 일자를 담을 리스트

        - game : 경기 스코어를 담을 리스트

    ● 경기 세부 내용에 사용되는 변수

        - all_lines : 구분을 편하게 하기 위해 일부러 둔 리스트

        - category : pandas frame에서 헤더가 될 set

        - match_team & match_team_2 : 경기 팀을 담을 리스트
        - Game_champion_level & Game_champion_level_2 : 챔피언 레벨 담을 리스트

           * 여기 아래에서 부터는 _2 부분을 생략하지만 리스트로 선언해줌

        - Game_player : 플레이한 선수 닉네임
        - Game_kda : 선수 KDA와 평점[ (킬+어시)/데스]
        - Game_damage : 입힌 피해량 

        - Game_sight : 시야 점수
        - Game_cs : cs
        - Game_per_cs : 분당 cs

import json
import pandas as pd
from sqlalchemy import create_engine

# 파일명 : LCK_YYYY_data_M.json
with open('LCK_2023_data_1.json', "r") as f:
    data = json.load(f)
    
# 변수 선언
	# 게임 정보
lines = list()			# 경기 일자/스코어(list[0]번째) 뽑고 저장하는 변수
date = list()			# 경기 일자
game = list()			# 게임 스코어(팀명 / 스코어 / 스코어 / 팀명)

	# 게임 세부 정보
all_lines = list()		# 상세 내용(list[1]번째) 뽑고 저장하는 변수
category = set() # 카테고리
# ================= 팀 1 ==================
match_team = list() 
Game_champion_level = list()
Game_player = list()
Game_kda = list()
Game_damage = list()
Game_sight = list()
Game_cs = list()
Game_per_cs = list()

#================== 상대팀 ======================
match_team_2 = list()
Game_champion_level_2 = list()
Game_player_2 = list()
Game_kda_2 = list()
Game_damage_2 = list()
Game_sight_2 = list()
Game_cs_2 = list()
Game_per_cs_2 = list()

 

○ 데이터 전처리

    ● 필요없는 Live, Channel, 1st 등의 문자열 제거

    ● 4등부터 11등까지는 th로 끝나므로 for문으로 추가

    ● 트러블 : 2023년 1월의 데이터를 보고 전처리를 진행하였음. 그런데 데이터가 꼬이는 현상이 발생

                     찾아 본 결과 2023년 4월부터 OP 포인트가 추가되었음 그래서 OP로 시작하는 단어도 지워줌

    ● 제거하는 코드

for i in range(0, len(data), 3):
    lines.append(data[i].split('\n'))

for i in range(0, len(data)):
    all_lines.append(data[i].split('\n'))
    
remove_list = ['Live', 'Channel', '1st', '2nd', '3rd','MVP', 'FB']
for i in range(4,12):
    rank = '{}th'.format(i)
    remove_list.append(rank)

# 'OP'로 시작하는 단어 추가
op_starting_words = [word for sublist in all_lines for word in sublist if word.startswith('OP')]

remove_list.extend(op_starting_words)

# 각 서브리스트에서 remove_list의 요소를 제거
filtered_lines = [
    [item for item in sublist if item not in remove_list]
    for sublist in lines
]

filtered_all_lines = [
    [item for item in sublist if item not in remove_list]
    for sublist in all_lines
]

 

○ 일자 및 경기 스코어 뽑기

# date 리스트와 game 리스트
date = [' '.join(sublist[0:2]) for sublist in filtered_lines]
game = [sublist[2:] for sublist in filtered_lines]

 

○ filtered_all_lines에서 데이터 추출

    ● category는 한 번만 더해주었음. Item 정보는 못 받아와서 Item이라는 문자를 공백으로 바꿔주면서 처리

    ● 세부 내용 뽑기(filtered_all _lines[1]에서)

        - 바깥쪽 for i in range(1, len(filtered_all_lines), 3)에서 3인 이유 : 리스트의 2번째 요소들만 사용하기 때문

        - sublist의 0 ~ 3번 인덱스 : 팀명 승리여부 진형(블루/퍼플)

        - sublist의 4번 인덱스 : 레벨
        - sublist의 5번 인덱스 : 닉네임
        - sublist의 6번 인덱스 : KDA
        - sublist의 7번 인덱스 : 평점
        - sublist의 8번 인덱스 : 데미지
        - sublist의 9번 인덱스 : 시야
        - sublist의 10번 인덱스 : cs
        - sublist의11번 인덱스 : 분당 cs

    ● match_team에서 for문을 돌릴 때 마다 1번씩만 더해줌

    ● 트러블 : 처음에 팀을 두 개로 안 나누고 한 번에 진행하여 값이 이상하게 저장됨

                     모든 팀의 정보가 들어가 있고 중간에 match_team에 들어갈 정보가 사이에 있기 때문

                     그래서 팀을 두 개로 나눠서 진행

    ● for문 반복

        - 44인 이유 : 첫 번째 팀의 마지막 인덱스가 44

        - 8만큼 더해주는 이유 : 한 선수의 정보가 8개가 들어있음

        - 트러블 : 처음에 cnt == 5로 안 끊어주니 마지막에 데미지와 시야 등 뒷 부분의 리스트들이 뒷 쪽의 데이터를 받아옴

                        그래서 5번만 반복하게 하고 break를 걸어줌

        -  데이터를 넣을 때  damage, sight, cs 등은 int형과 float형으로 받아옴

    ● 두 번째 팀 for문 반복

        - 세부 내용은 처음 팀과 같지만 44번 인덱스 부터 두 번째 팀 정보 시작

        - 시작 하는 부분이 다른 것을 제외하고는 로직은 동일

category.add(' '.join(filtered_all_lines[1][3].replace('Items', '').split()))

# 월 게임 총 49번 for문
for i in range(1, len(filtered_all_lines), 3):
    cnt = 1
    # 49개의 팀, 1번째 팀의 진형
    match_team.append(' '.join(filtered_all_lines[i][0:3])) # 팀명 승리 진형
    # 세트 진행
    for j in range(1, 44, 8):
            filtered_all_lines
            # print(j)
            Game_champion_level.append(int(filtered_all_lines[i][j+3]))
            Game_player.append(filtered_all_lines[i][j+4])
            Game_kda.append(filtered_all_lines[i][j+5] + ' ' + filtered_all_lines[i][j+6])
            if cnt == 5:
                Game_damage.append(int(filtered_all_lines[i][40].replace(',', '')))
                Game_sight.append(int(filtered_all_lines[i][41]))
                Game_cs.append(int(filtered_all_lines[i][42]))
                Game_per_cs.append(float(filtered_all_lines[i][43]))
            else:
                Game_damage.append(int(filtered_all_lines[i][j+7].replace(',', '')))
                Game_sight.append(int(filtered_all_lines[i][j+8]))
                Game_cs.append(int(filtered_all_lines[i][j+9]))
                Game_per_cs.append(float(filtered_all_lines[i][j+10]))
            # print('cnt :' + str(cnt))
            cnt += 1
            if cnt >= 6:
                break
# print()
# 월 게임 총 49번 for문  
for j in range(1, len(filtered_all_lines), 3):
    # 49개의 팀, 2번째 팀의 진형
    match_team_2.append(' '.join(filtered_all_lines[j][44:47]))
    cnt2 = 1

    for k in range(44, len(filtered_all_lines[1]), 8):
            Game_champion_level_2.append(int(filtered_all_lines[j][k+4]))
            Game_player_2.append(filtered_all_lines[j][k+5])
            Game_kda_2.append(filtered_all_lines[j][k+6] + ' ' + filtered_all_lines[j][k+7])
            if cnt2 == 5:
                Game_damage_2.append(int(filtered_all_lines[j][-4].replace(',', '')))
                Game_sight_2.append(int(filtered_all_lines[j][-3]))
                Game_cs_2.append(int(filtered_all_lines[j][-2]))
                Game_per_cs_2.append(float(filtered_all_lines[j][-1]))
            else:
                Game_damage_2.append(int(filtered_all_lines[j][k+8].replace(',', '')))
                Game_sight_2.append(int(filtered_all_lines[j][k+9]))
                Game_cs_2.append(int(filtered_all_lines[j][k+10])) 
                Game_per_cs_2.append(float(filtered_all_lines[j][k+11]))
            # print('cnt2 :' + str(cnt2))
            cnt2 += 1
            if cnt2 >= 6:
                break

 

○ 값이 잘 나오는 지 확인

 

    ● 서로 인덱스 길이가 같아야 하는 요소들 * 아래 출력 결과 인덱스 값들이 전부 동일해야함

         - Game_champion_level & Game_champion_level_2
         - Game_player & Game_player_2

         - Game_kda & Game_kda_2

         - Game_sight & Game_sight_2
         - Game_cs & Game_cs_2
     

    ● category & match_team_2 & match_team & date & game
      : 위의 요소 * 5 했을 때 첫번째에 적힌 요소들의 값과 동일해야함

len(Game_champion_level), len(Game_player), len(Game_kda), len(Game_damage), len(Game_sight), len(Game_cs)

len(Game_champion_level_2), len(Game_player_2), len(Game_kda_2), len(Game_damage_2), len(Game_sight_2), len(Game_cs_2)

len(match_team_2), len(match_team), len(date), len(game), len(date)

 

○ 데이터 프레임

    ● DB 생성 및 연동 되어 있어야 함

    ● urstory와 같은 계정 생성
    ● Seasons 변화를 row의 Season의 value를 변경해줘야함(Season은 직접 입력 해줬음)
        - 2023 1/18 ~ 4/9  #2023_Spring
        - 2023 6/7 ~ 8/20? #2023_Summer
        - 2024 1/17 ~ 4/14 #2024_Spring
        - 2023 6/12 ~ 8/18? #2023_Summer

    ● team, team_score, opponent_score, opponent_team

         - game 리스트의 sublist의 0 ~ 3까지 순서대로 넣어주기

    ● date, team의 정보를 1번 넣을 때 나머지 데이터를 5개를 넣어야함(한 경기에 한 팀의 선수가 5명) 

import pandas as pd

# 각 date와 game에 해당하는 정보를 담을 리스트
rows = []

for i in range(len(date)):
    # 반복을 통해서 0번 인덱스, 1번 인덱스 등을 생성
    for j in range(5):
        rows.append({
            'Season': '2023_Spring',
            'Date': date[i],
            'Team': game[i][0],
            'Team_Score': game[i][1],
            'Opponent_Score': game[i][2],
            'Opponent_Team': game[i][3],
            'Champion': Game_champion_level[i * 5 + j],
            'Opponent_Champion': Game_champion_level_2[i * 5 + j],
            'Player': Game_player[i * 5 + j],
            'Opponet_Player': Game_player_2[i * 5 + j],
            'KDA': Game_kda[i * 5 + j],
            'Opponent_KDA': Game_kda_2[i * 5 + j],
            'Damage': Game_damage[i * 5 + j],
            'Opponent_Damage': Game_damage_2[i * 5 + j],
            'Sight': Game_sight[i * 5 + j],
            'Opponent_Sight': Game_sight_2[i * 5 + j],
            'CS': Game_cs[i * 5 + j],
            'Opponent_CS': Game_cs_2[i * 5 + j],
            'Per_CS': Game_per_cs[i * 5 + j],
            'Opponent_Per_CS': Game_per_cs_2[i * 5 + j]
        })

# DataFrame 생성
df = pd.DataFrame(rows)

 

○ DB 연결

def send_2_mysql(df, p_name):
    # MySQL 데이터베이스 연결 정보 설정
    user = 'urstory'
    password = 'u1234'
    host = 'localhost'
    database = 'LCK_Practice'

    # SQLAlchemy 사용
    engine = create_engine(f'mysql+mysqlconnector://{user}:{password}@{host}/{database}')

    # 데이터프레임을 MySQL 테이블로 저장
    table_name = p_name
    df.to_sql(name=table_name, con=engine, if_exists='replace', index=False)

send_2_mysql(df, '2023_Spring_1')

* Django와 연동까지 완료한 상태에서 찍은 것이기에 다른 테이블도 존재함

 

다음 번에는 html, js, css, POST방식 연결등을 한 Django 코드를 리뷰하겠습니다.