경기 상세 내용을 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 코드를 리뷰하겠습니다.
'Networks > Project' 카테고리의 다른 글
SK networks AI Camp - mini Project2(Active Senior) (0) | 2024.08.19 |
---|---|
SK networks AI Camp - Toy Project 크롤링 코드(2) 및 전처리 리뷰 (0) | 2024.08.15 |
SK networks AI Camp - Toy Project 크롤링 코드 리뷰 (4) | 2024.08.10 |
SK networks AI Camp - Toy Project(에자일_1) (0) | 2024.08.10 |
SK networks AI Camp - 야구 데이터 분석하기(2) feat. Lotte giants (0) | 2024.08.05 |