KBO 크롤링하기-2
2002-2024년
앞에서 2001년까지 수행했으므로 이제 2002-2024년의 데이터를 크롤링하려고 한다.
구조 확인
현재 클릭해야하는 버튼은 크게 3가지다.
1. 다음 기록
2. 세부기록
3. 두 번째 페이지
그렇기 때문에 순서를
기본 페이지 -> 다음 기록 페이지 -> 세부 기록 페이지 -> 행 합치기 -> 2번 째 페이지(기본 페이지) -> 다음 기록 페이지 -> 세부기록 페이지 -> 행 합치기 -> 1번 페이지, 2번 페이지 열 합치기
순서로 했다.
크롤링 시작
첫 번째 글에서 만든 내용을 바탕으로 조금 수정했다.
def page_click(driver):
page_count = len(driver.find_elements(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.record_result > div > a'))
df1 = create_table(driver)
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.more_record > a.next').click()
df2 = create_table(driver)
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.tab-depth3 > ul > li:nth-child(2) > a').click()
df3 = create_table(driver)
page_1_df = pd.concat([df1, df2, df3], axis=1)
if page_count > 1:
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ucPager_btnNo2').click()
df1 = create_table(driver)
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.more_record > a.next').click()
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ucPager_btnNo2').click()
df2 = create_table(driver)
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.tab-depth3 > ul > li:nth-child(2) > a').click()
time.sleep(.2)
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ucPager_btnNo2').click()
df3 = create_table(driver)
page_2_df = pd.concat([df1, df2, df3], axis=1)
return pd.concat([page_1_df, page_2_df])
else:
return page_1_df
하지만 여기서 문제가 발생했다.
ElementClickInterceptedException, StaleElementReferenceException
ElementClickInterceptedException, StaleElementReferenceException 둘 다 비슷하지만 조금 다르다
StaleElementReferenceException는 현재 element가 없거나 찾을 수 없을 때 나타나며
ElementClickInterceptedException는 해당 버튼이나 요소가 다른 것에 가려저 사용할 수 없을 때 나타난다.
StaleElementReferenceException는 time.sleep을 이용하여 해결했지만 ElementClickInterceptedException는에서 지속적으로 오류가 발생했다.
나중에는 ipython, execute 등의 오류가 뜨면서 작동하지 않았다.
def base_click(max_attempts, driver):
time.sleep(1)
for attempt in range(max_attempts):
try:
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.tab-depth3 > ul > li:nth-child(1) > a'))).click()
break
except StaleElementReferenceException as e:
if attempt == max_attempts - 1:
raise
time.sleep(.5)
except ElementClickInterceptedException as e:
print('기본 기록 클릭할 수 없음')
if attempt == max_attempts - 1:
raise
time.sleep(1)
def next_click(max_attempts, driver):
time.sleep(1)
for attempt in range(max_attempts):
try:
# wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.more_record > a.next'))).click()
next_record = driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.more_record > a.next')
driver.execute_script('arguments[0].click()', next_record)
break
except StaleElementReferenceException as e:
if attempt == max_attempts - 1:
raise
time.sleep(.5)
except ElementClickInterceptedException as e:
print('다음 기록 클릭할 수 없음')
if attempt == max_attempts - 1:
raise
time.sleep(1)
def detail_click(max_attempts, driver):
time.sleep(1)
for attempt in range(max_attempts):
try:
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.row > div.tab-depth3 > ul > li:nth-child(2) > a'))).click()
break
except StaleElementReferenceException as e:
if attempt == max_attempts - 1:
raise
time.sleep(.5)
except ElementClickInterceptedException as e:
print('세부 기록 클릭할 수 없음')
if attempt == max_attempts - 1:
raise
time.sleep(1)
로딩 될 때까지 10초까지 기다리거나, 오류가 났을 때 1초 뒤에 다시 실행하거나 자바 스크립트를 직접 클릭하는 방법을 시도했지만 모두 실패했다.
해결
그래서 나는 버튼을 눌러 페이지를 넘어가 데이터를 모으는 방법 말고, 페이지마다 데이터를 모아 합치는 것을 선택했다.
현재 눌러야 되는 버튼은 크게 3가지라고 했다.
그리고 그 페이지마다 하나씩 url이 존재했다.
urls = ['https://www.koreabaseball.com/Record/Player/HitterBasic/Basic1.aspx',
'https://www.koreabaseball.com/Record/Player/HitterBasic/Basic2.aspx',
'https://www.koreabaseball.com/Record/Player/HitterBasic/Detail1.aspx']
이 페이지 하나씩 돌아서 1, 2페이지를 모으고 그렇게 모은 3가지의 페이지를 하나로 합치는 방법을 선택했다.
def page_click(driver):
# 페이지 개수 확인
page_count = len(driver.find_elements(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_udpContent > div.record_result > div > a'))
# 페이지 크롤링
df = create_table(driver)
if page_count > 1:
time.sleep(.2)
# 다음 페이지 넘어가기
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ucPager_btnNo2').click()
time.sleep(.2)
# 페이지 크롤
df2 = create_table(driver)
# 1, 2페이지 데이터 합치기
df = pd.concat([df, df2])
time.sleep(.2)
# 1 페이지로 돌아가기
driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ucPager_btnNo1').click()
return df
record_dfs = []
seasons = [str(i) for i in range(2002, 2025)]
for url in urls:
driver.get(url)
dfs = []
for season in seasons:
# 시즌 콤보 박스 선택
season_combo = driver.find_element(By.CSS_SELECTOR, '#cphContents_cDwdphContents_cphContents_ddlSeason_ddlSeason')
# 시즌 콤보 박스 선택
season_combo = Select(season_combo)
# 값 선택
season_combo.select_by_value(season)
teams = team_list(driver)
for team in teams:
time.sleep(.5)
combobox = driver.find_element(By.CSS_SELECTOR, '#cphContents_cphContents_cphContents_ddlTeam_ddlTeam')
team_combo = Select(combobox)
team_combo.select_by_visible_text(team)
time.sleep(.5)
df = page_click(driver)
df['year'] = season
dfs.append(df)
time.sleep(10)
print(season,'년 완료!')
record_dfs.append(dfs)
# 인덱스 안맞음 방지
df1 = pd.concat(record_dfs[0]).reset_index()
df2 = pd.concat(record_dfs[1]).reset_index()
df3 = pd.concat(record_dfs[2]).reset_index()
df = pd.concat([df1, df2, df3], axis=1)
# 필요없는 데이터 제거
df.drop(['BB/K볼넷/삼진', 'GO땅볼', 'ISOP순수장타율', 'GO/AO땅볼/뜬공', 'HR홈런', 'OBP출루율', 'BB볼넷'], axis=1, inplace=True)
df.loc[:, ~df.columns.duplicated()].to_csv('2002_2024.csv', encoding='cp949')