이전에 진행했던 코드에서 챔피언에 관한 정보가 들어 있지 않고 단순히 챔피언 레벨만 입력이 되어 있었습니다.
img의 alt값으로 챔피언 정보가 들어 있다는 것을 알게 되었고 alt값을 뽑아오도록 코드를 작성하였습니다.
2024.08.10 - [Networks/Project] - SK networks AI Camp - Toy Project 크롤링 코드 리뷰
이전에 진행했던 코드가 모듈화가 되어있었기에 뽑아오는 데이터만 수정해줘서 코드를 금방 작성하고 완료 가능했습니다.
나머지 함수들은 그대로 사용(click_month, select_month, find_month_child, find_day_child, find_set_child, go_to_2023, go_to_2024, push_2024_game 등)했습니다.
push_2024_set 함수에서 XPath에 해당하는 데이터를 text로 받는 부분만 수정.
뽑아야 하는 데이터의 XPath 경로로 변경
[push_2024_set 함수]
○ play_time_data : 경기 소요 시간이 들어 있는 부분의 경로
○ all_ban_data : 벤픽으로 선택한 챔피언 이미지가 있는 부분 경로
○ all_play_data : 선수들이 선택한 챔피언, 스펠, 룬, 아이템의 이미지가 있는 부분의 경로
○ img 태그에서 alt 값 추출
● all_ban_data와 all_play_data만 이미지에서 alt값 추출
● parent_element 변수에 해당 요소를 찾아서 저장
● img_tags 변수에 parent_element 에 있는 img 태그를 찾기(By.Tag_NAME 으로 태그 이름으로 찾기)
● for문을 돌려서 img_tag에 img_tags 요소들을 넣어서 'alt' attribute를 찾아서 리스트에 추가
○ 트러블
● No such Element error가 계속 발생
● 멈춘 부분에서 XPath를 찾아보니 기존 변수에 선언해준 XPath와 달랐음
- error 발생 시(except 사용) play_time_data를 다른 XPath 태그로 재선언 해줘서 해결
- all_ban_data, all_play_data는 XPath가 동일
# 게임 내에서 set 번호 누르기
def push_2024_set(p_child):
set_num = find_set_child(p_child)
driver.implicitly_wait(5)
time.sleep(1.5)
# /html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[1]/div/div[1]/div[2]/div[1]
play_time_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[2]/div/div[1]/div[2]'
# /html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[1]/div/div[1]/div[3]
all_ban_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[2]/div/div[1]/div[3]'
# /html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[2]/div/div[2]/div[1]/div[2]/div/ul
all_play_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[2]/div/div[2]/div[1]/div[2]/div/ul'
for k in range(1, set_num + 1):
try:
driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/main/div[1]/div[2]/ul/button[{}]'.format(k)).click()
driver.implicitly_wait(10)
time.sleep(7)
except NoSuchElementException:
driver.refresh()
driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/main/div[1]/div[2]/ul/button[{}]'.format(k)).click()
driver.implicitly_wait(10)
time.sleep(5)
finally:
# 값 추가
try:
lck_2024_data.append(driver.find_element(By.XPATH, play_time_data).text)
# 지정된 경로에서 먼저 해당 요소를 찾습니다
parent_element = driver.find_element(By.XPATH, all_ban_data)
# 해당 요소 내에서 모든 img 태그를 찾습니다
img_tags = parent_element.find_elements(By.TAG_NAME, 'img')
# 각 img 태그의 alt 속성을 추출하여 리스트에 추가합니다
for img_tag in img_tags:
alt_text = img_tag.get_attribute('alt')
lck_2024_data.append(alt_text)
# print(lck_2024_data)
# 지정된 경로에서 먼저 해당 요소를 찾습니다
parent_element = driver.find_element(By.XPATH, all_play_data)
# 해당 요소 내에서 모든 img 태그를 찾습니다
img_tags = parent_element.find_elements(By.TAG_NAME, 'img')
# 각 img 태그의 alt 속성을 추출하여 리스트에 추가합니다
for img_tag in img_tags:
alt_text = img_tag.get_attribute('alt')
lck_2024_data.append(alt_text)
lck_2024_data.append('='*50)
driver.implicitly_wait(5)
time.sleep(1.5)
except NoSuchElementException:
driver.refresh()
play_time_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[1]/div/div[1]/div[2]/div[1]'
lck_2024_data.append(driver.find_element(By.XPATH, play_time_data).text)
all_ban_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[1]/div/div[1]/div[3]'
# 지정된 경로에서 먼저 해당 요소 찾기
parent_element = driver.find_element(By.XPATH, all_ban_data)
# 해당 요소 내에서 모든 img 태그를 찾기
img_tags = parent_element.find_elements(By.TAG_NAME, 'img')
# 각 img 태그의 alt 속성을 추출하여 리스트에 추가
for img_tag in img_tags:
alt_text = img_tag.get_attribute('alt')
lck_2024_data.append(alt_text)
# print(lck_2024_data)
# /html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[2]/div/div[2]/div[1]/div[2]/div/ul
all_play_data = '/html/body/div[1]/div[2]/div/main/div[2]/div/div[2]/div[1]/div/div[2]/div[1]/div[2]/div/ul'
# 지정된 경로에서 먼저 해당 요소 찾기
parent_element = driver.find_element(By.XPATH, all_play_data)
# 해당 요소 내에서 모든 img 태그를 찾기
img_tags = parent_element.find_elements(By.TAG_NAME, 'img')
# 각 img 태그의 alt 속성을 추출하여 리스트에 추가
for img_tag in img_tags:
alt_text = img_tag.get_attribute('alt')
lck_2024_data.append(alt_text)
lck_2024_data.append('='*50)
driver.implicitly_wait(5)
time.sleep(1.5)
# finally:
# lck_2024_data_2.append(game_time)
# lck_2024_data_2.append(detail)
# lck_2024_data_2.append('='*50)
# 끝나면 다시 원래 화면으로 돌아가기
driver.get(url)
driver.implicitly_wait(5)
time.sleep(2)
크롤링이 끝난 후 파일명은 기존 파일과 구분하여 LCK_년도_alt_data_월.json 파일로 저장
아래 사진과 같이 저장된 파일에서 데이터가 정렬이 안되어 있어 보기 편하게 정리하여 저장
* 리스트 하나에 모든 정보가 들어가 있어서 보기가 불편함
데이터 전처리 후 파일을 저장(모듈화하여 코드 작성)
○ 전처리 전 데이터 형식
● data[0] : 경기시간 \n 팀1 \n 킬스코어1 \n 킬스코어2 \n 팀2 \n 팀 플레이어 \n MVP
● data[1:6] : 1 ~ 5까지 벤 챔피언 5개
● data[6:11] : 6 ~ 10까지 벤 챔피언 5개
● data[11] : 팀1
● data[12] : 챔피언
● data[13:15] : 13, 14 스펠
● data[15:17] : 15 주룬, 16 보조룬
● data[17:24] : 17 ~ 22 아이템 6개 + 23 와드
● 트러블 : 선수의 아이템 갯수가 다름 why? 아이템 창 빈 곳의 경우 img가 존재하지 않음
- 그래서 리스트로 나눠서 담아놓고 전처리를 진행
- '=' * 50을 기준으로 나눠서 저장
○ 정리할 데이터 양식
● List[i][0] : 경기 시간
● List[i][1] : 경기 스코어; 팀명1 킬스코어1 킬스코어2 팀명2
● List[i][2] : MVP 선수 정보 : MVP 팀 닉네임
● List[i][3] : 벤픽한 챔피언 10개
● List[i][4] : 챔피언/룬/스펠/아이템 정보
[(맨 앞에만 팀명)챔프 스펠1 스펠2 룬1 룬2 아이템]* 5 [(맨 앞에만 팀명)챔프 스펠1 스펠2 룬1 룬2 아이템]* 5
※ 아이템 창을 꽉 채워서(6개+1개:와드) 안 한 경우도 있음
○ 함수 살펴보기
● parse_first_string : 첫 번째 문자열 처리 함수
- 받은 인자(data_str)을 '\n'으로 구분하여 parts 변수에 저장
- game_time 변수에 parts의 0번째 인덱스를 넣어 시간 저장
- game_score 변수에 공백을 추가하여 parts[1]부터 parts[4]까지 넣어 하나의 문자열로 경기 스코어를 저장
- mvp_team_player 변수에 MVP 팀 선수명 형식으로 저장
- game_time, game_score, mvp_team_player을 return
● combine_strings : 문자열을 공백으로 받은 인자(count)만큼 결합
- 받은 인자 data의 시작 인덱스 부터 count개 만큼 공백을 추가하여 결합
● make_champion_information : '=' * 50개 전까지의 데이터를 공백으로 결합
- 받은 인자(start_idx)부터 받은 인자(data)의 길이만큼 for문을 돌림
- 만약 받은 data의 j번째가 구분자('='*50으로 인자로 전달받음)랑 같으면 값과 인덱스 번호 j를 return
- 예외로 구분자가 없는 경우 ''와 마지막 인덱스인 len(data)를 return
● process_data : 결과 리스트 생성
- split_list : 결과를 저장할 리스트
- data의 길이까지 while문을 반복
- temp_list : 임시 리스트는 while문 안에 둬서 매번 초기화
- game_time, game_score, mvp_team_player 변수에 parse_first_string 함수가 return한 값을 저장
- 임시 리스트인 temp_list에 하나씩 저장
- combined_string에 값을 넘겨 combine_strings 함수가 return한 문자열을 저장하고 temp_list에 저장
- combined_until_separator와 next_idx 변수에 make_champion_information 함수의 return 값을 저장
- 임시 리스트 temp_list에 combined_until(챔피언, 룬, 스펠, 아이템 정보) 저장
- 결과 리스트인 split_list에 임시 리스트에 저장
- i 변수에 next_idx + 1을 하여 다음 게임 정보가 시작하는 인덱스를 저장
- while문이 끝난 후 결과를 저장한 split_list를 return
import json
def parse_first_string(data_str):
"""첫 번째 문자열 처리"""
parts = data_str.split('\n')
# print(parts)
game_time = parts[0]
game_score = ' '.join(parts[1:5])
mvp_team_player = f"{parts[-1]} {parts[-2]}"
return game_time, game_score, mvp_team_player
def combine_strings(data, start_idx, count):
"""문자열을 공백으로 count개 결합"""
return ' '.join(data[start_idx:start_idx+count])
def make_champion_information(data, start_idx, separator):
""" '='*50 전까지의 데이터를 공백으로 결합"""
for j in range(start_idx, len(data)):
if data[j] == separator:
return ' '.join(data[start_idx:j]), j
return '', len(data) # 구분자를 찾지 못한 경우
def process_data(data):
"""결과 리스트 생성"""
split_list = []
i = 0
while i < len(data):
temp_list = []
# print(i)
# print(data[i]) #############################
# 첫 번째 문자열을 나누기 => 시간, 스코어, MVP로
game_time, game_score, mvp_team_player = parse_first_string(data[i])
temp_list.append(game_time)
temp_list.append(game_score)
temp_list.append(mvp_team_player)
# 벤픽
combined_string = combine_strings(data, i+1, 10)
temp_list.append(combined_string)
# 챔피언과 아이템 정보
combined_until_separator, next_idx = make_champion_information(data, i+11, '='*50)
temp_list.append(combined_until_separator)
# 완성된 서브리스트를 결과 리스트에 추가
split_list.append(temp_list)
i = next_idx + 1
#print('='*50)
return split_list
● Json을 열고 파일을 저장하는 함수
- open_json과 save_json 함수에 이름을 바꿔가면서 파일을 열고 파일을 저장하기 위해 format을 사용
- 연도(y), 월(i)를 넣어가면서 for문을 사용하여 한 번에 할 예정
# Json 열기
def open_json(y, i, data):
with open('LCK_{}_alt_data_{}.json'.format(y, i), "r") as f:
data = json.load(f)
return data
# Json 저장하기
def save_json(y,i, split_list):
# json 파일로 저장
with open('Pre_LCK_{}_alt_data_{}.json'.format(y,i), 'w', encoding='utf-8') as f :
json.dump(split_list, f)
○ 함수를 사용하여 결과 받기
● 2023년 데이터
- 1월부터 8월까지 존재(5월은 존재하지 않음)
- open_json 함수에 2023(y), 월(i : 1 ~ 8), data(빈 리스트)를 줘서 해당 json 파일을 저장한 data 리스트를 return 받음
- process_data에 저장한 data를 넘겨주고 split_list(저장된 리스트)를 반환 받아 split_list에 저장
- save_json에 2023(y), 월(i : 1 ~ 8), split_list(데이터)를 넘겨주고 json 파일로 저장
● 2024년 데이터
- 1월부터 7월까지 존재(5월은 존재하지 않음)
- open_json 함수에 2024(y), 월(i : 1 ~ 7), data(빈 리스트)를 줘서 해당 json 파일을 저장한 data 리스트를 return 받음
- process_data에 저장한 data를 넘겨주고 split_list(저장된 리스트)를 반환 받아 split_list에 저장
- save_json에 2024(y), 월(i : 1 ~ 7), split_list(데이터)를 넘겨주고 json 파일로 저장
● 트러블
- 2024년 4월 데이터를 받아와 parse_first_stirng 함수에서 나누는 도중에 List Index Out of Range 에러 발생
- 어디서 에러가 발생했는 지 보기 위해 j를 인자에 추가하여 print
- 49번째에서 에러발생 → JSON 파일을 살펴보니 첫 줄에 경기 시간만 들어옴
- Crawling 중간에 에러가 발생한 것으로 보임
- 누락된 값이 얼마 되지 않는 것으로 확인하여 그냥 데이터를 같은 형식으로 수정
- 6/26 T1 2 0 DRX 경기부터 에러발생
- 바꾼 데이터(in Json)
성공한 데이터들 예시
"24:07\nKT\n3\n18\nHLE\nHLE Zeka\nMVP",
실패한 데이터
"34:24",
변경해준 데이터들
6/26 T1 2 0 DRX 경기
○ 1set
"34:24\nT1\n11\n7\nDRX\nT1 Oner\nMVP",
○ 2set
"21:08\nT1\n9\n2\nDRX\nT1 Keria\nMVP",
6/26 19:30 HLE 0 2 GEN 경기
○ 1set
"32:53\nGEN\n14\n6\nHLE\nGEN Peyz\nMVP",
○ 2set
"42:08\nHLE\n16\n19\nGEN\nGEN Canyon\nMVP",
6/27 17:00 NS 0 2 DK 경기
○ 1set
"25:17\nNS\n7\n21\nDK\nDK Kingen\nMVP",
○ 2set
"26:30\nDK\n13\n7\nNS\nDK Lucid\nMVP",
6/27 19:30 KT 2 1 BRO 경기
○ 1set
"29:15\nKT\n20\n13\nBRO\nKT PerfecT\nMVP",
○ 2set
"37:56\nBRO\n18\n17\nKT\nBRO YoungJae\nMVP",
○ 3set
"25:18\nKT\n26\n4\nBRO\nKT Pyosik\nMVP",
# 2023년 데이터
for i in range(1, 9):
split_list = list()
data = list()
if i == 5:
continue
else:
data = open_json(2023, i, data)
split_list = process_data(data)
save_json(2023, i, split_list)
# 2024년 데이터
for i in range(1, 8):
split_list = list()
data = list()
if i == 5:
continue
else:
data = open_json(2024, i, data)
split_list = process_data(data)
save_json(2024, i, split_list)
○ 출력 결과
저장한 Pre_LCK_년도_alt_data_월.json 파일을 전처리한 이 후 코드 리뷰를 진행하겠습니다.
'Networks > Project' 카테고리의 다른 글
SK networks AI Camp - toyproject AWS 및 Github 트러블 이슈 (5) | 2024.10.17 |
---|---|
SK networks AI Camp - mini Project2(Active Senior) (0) | 2024.08.19 |
SK networks AI Camp - Toy Project 전처리 코드 리뷰 (0) | 2024.08.11 |
SK networks AI Camp - Toy Project 크롤링 코드 리뷰 (4) | 2024.08.10 |
SK networks AI Camp - Toy Project(에자일_1) (0) | 2024.08.10 |