저번 포스팅에 이어서 이번에는 특정 페이지를 크롤링한 후 이 데이터를 캘린더에 추가해보고 삭제까지 진행해보았다.
이번에 크롤링 할 사이트는 KBO(프로야구)의 일정 중 '기아타이거즈' 팀의 일정을 크롤링 해보았다.
크롤링은 BeatuifulSoup, Selenium을 이용하여 진행했다. (참고한 블로그)
이전에 크롬 웹 드라이버를 사용했던 적이 있어서 그때 사용한 크롬 웹 드라이버를 그대로 썼더니 오류가 발생했다.
이전에 윈도우OS에서 작업하던 파일을 그대로 가져다 쓰다보니 오류가 났다. 이 경우 맥버전을 다시 다운로드 해주면 된다.
위 오류에 관한 해결방법은 링크된 블로그에 친절히 나와있다.
# 정규 시즌 콤보 박스 클릭
driver.find_element(by=By.XPATH, value="//select[@id='ddlSeries']/option[text()='KBO 정규시즌 일정']").click()
왼쪽에 보이는 콤보박스에 각각 시범경기, 정규시즌, 포스트시즌 일정이 있다.
이번엔 정규시즌 일정을 가져올 예정이므로 ddlSeries
로 되어있는 id 값을 이용해 콤보박스를 정규시즌으로 설정해준다.
# 기아 선택
driver.find_element(by=By.XPATH, value="//ul[@class='tab-schedule']/li[@attr-value = 'HT']").click()
그리고 KIA의 일정만 보기 위해서 팀 선택을 해준다.
단, 옛 구단이름인 해태에서 나온것으로 보이는 HT
로 되어있는 부분을 주의한다.(아직도..? 🫢)
def convert_date(game_date: str):
month = int(game_date[0:2])
day = int(game_date[3:5])
return {"month": month, "day": day}
def convert_team_data(team_data: str):
team_data = team_data.replace("vs", " vs ")
return "".join(re.compile("[^0-9]").findall(team_data))
def crawling_kia_tigers():
driver = webdriver.Chrome("./chromedriver")
driver.get('https://www.koreabaseball.com/Schedule/Schedule.aspx')
# 정규 시즌 콤보 박스 클릭
driver.find_element(by=By.XPATH, value="//select[@id='ddlSeries']/option[text()='KBO 정규시즌 일정']").click()
# 기아 선택
driver.find_element(by=By.XPATH, value="//ul[@class='tab-schedule']/li[@attr-value = 'HT']").click()
month_list = ['05']
schedule_list = []
for month in month_list:
# 달력 선택
driver.find_element(by=By.XPATH, value="//select[@id='ddlMonth']/option[text()='"+str(month)+"']").click()
# 결과
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
table = soup.find('table', {'class': 'tbl'})
trs = table.find_all('tr')
for idx, tr in enumerate(trs):
if idx > 0:
tds = tr.find_all('td')
# tds[0] 날짜, tds[1] 시간, tds[2] 팀 + 스코어, tds[7] 장소
date = convert_date(tds[0].text.strip())
schedule_info = {
"date": tds[0].text.strip(),
"month": date["month"],
"day": date["day"],
"begin_time": tds[1].text.strip(),
"end_time": f"{(int(tds[1].text.strip()[0:2]) + 3)}:{tds[1].text.strip()[3:5]}",
"team": convert_team_data(tds[2].text.strip()),
"stadium": tds[7].text.strip(),
}
schedule_list.append(schedule_info)
driver.close()
return schedule_list
일정을 가져온 다음 구글 캘린더에 일정을 추가해야되는 부분을 생각하여 그에 맞춰 데이터를
schedule_info
에 정리해서 담아준다.
월, 일을 구분하기 위하여 따로 month
,day
에 나눠서 담아주고,
홈페이지엔 시작시간만 기본적으로 제공하는데 (야구는 시간이 정해진 스포츠가 아니기 때문)
대략 3시간으로 끝나는 시간을 잡아서 end_time
에 넣어줬다.
그리고 tds[2]
에서 가져오는 데이터는 팀vs팀 형태로 나오는데 아직 경기를 치르지 않은 경우는
KIAvsNC
이런 형태로 나오지만 이미 경기를 치뤄서 스코어가 나와있는 경우엔
KIA13vs2한화
의 형태로 가져오게 되어서 스코어는 삭제처리하게끔 하였다.
def set_game_schedule(schedule_list: list):
google_calendar = GoogleCalendar()
for schedule in schedule_list:
date = datetime.date(year=2022, month=schedule["month"], day=schedule["day"]).isoformat()
event = {
"summary": schedule["team"], # 일정 제목
"location": schedule["stadium"], # 일정 장소
"description": '야구 경기', # 일정 설명
"start": { # 시작 날짜
"dateTime": date + f"T{schedule['begin_time']}:00",
"timeZone": "Asia/Seoul",
},
"end": { # 종료 날짜
"dateTime": date + f"T{schedule['end_time']}:59",
"timeZone": "Asia/Seoul",
},
}
google_calendar.set_google_calendar(event)
크롤링한 데이터를 가져와서 캘린더에 반복문으로 추가해준다.
생각보다 추가하는데 시간이 꽤 걸린다. 그래서 시즌 전체 일정을 추가하려고 하였으나 5월 일정만 추가하였다.
(위의 크롤링 코드에 5월만 있다.)
캘린더에 잘 추가된 모습.
def get_total_google_calendar(self):
creds = self.renew_google_token()
service = build('calendar', 'v3', credentials=creds)
page_token = None
event_list = []
while True:
events = service.events().list(calendarId=config.calendar_id, pageToken=page_token).execute()
for event in events['items']:
event_list.append(event)
page_token = events.get('nextPageToken')
if not page_token:
break
return event_list
def delete_google_calendar(self, event_id):
creds = self.renew_google_token()
service = build('calendar', 'v3', credentials=creds)
service.events().delete(calendarId=config.calendar_id, eventId=event_id).execute()
이전에 만들어두었던 클래스에 두 가지를 추가하였다.
전체일정을 가져오는 함수와 일정의 id값으로 삭제 하는 함수를 추가해준다.
def delete_game_schedule():
google_calendar = GoogleCalendar()
game_list = google_calendar.get_total_google_calendar()
for game in game_list:
if game.get("description") and game["description"] == "야구 경기":
google_calendar.delete_google_calendar(game["id"])
그리고 description
이 야구 경기로 되어있는 경우에 전체 삭제를 진행해준다.