이번에 ICT 학점연계 인턴십으로 총 3개의 회사에 지원하였다. 그래서 10일에 코딩테스트를 응시하고, 서류 결과와 면접 일정이 나오는 것을 기다리고 있다. 12 ~ 26일 사이에 서류 결과와 면접을 진행하게 되는데, 확인할 때마다 페이지에 접속해서 확인하기가 귀찮았다. 그래서 일정 시간마다 Slack으로 인턴십 페이지의 업데이트 유무를 알려주도록 자동화를 진행해 보았다.
ICT 학점연계 인턴십 결과 확인 자동화
우선 간단하고 빠르게 만들려고 한 것이기 때문에 환경 변수 지정이나 예외 처리는 따로 진행하지 않았고, 기능 구현을 목표로 진행하였다.
- 사용한 기술 및 프레임워크
- Server : Amazon EC2 (Ubuntu Server 24.04 LTS - Free Tier)
- Scraping : Python (3.12.3), Selenium (4.22.0), requests (2.31.0), pandas (2.2.2)
- Info Checking : Slack (Webhooks)
스크래핑 진행 과정
ICT 인턴십 페이지의 데이터 가져오기
- ICT 인턴십 페이지 (https://www.ictintern.or.kr/main.do)
코드를 작성하기에 앞서 어떻게 원하는 데이터까지 접근할 수 있는지 순서를 정리하였다.
- 페이지 접속 및 로그인 : ID와 PW를 입력한 뒤 "ENTER" 입력으로 로그인 진행
- 알림창 제거 : 로그인을 진행하면 아래와 같이 알림창이 떠서 제거해줘야 함
- 인턴십관리 접속 : 로그인 후 인턴십관리 페이지에서 지원 현황 확인 가능
- 데이터 추출 : 추출할 데이터는 기업명, 지원상태, 매칭상태, 면접일자
추출한 데이터를 DataFrame 형태로 변환
- 리스트 형태로 추출한 요소(기업명, 지원상태, 매칭상태, 면접일자)를 DataFrame 형태로 변환
- 각 기업 별로 관리하기가 수월해짐 -> 기업이 3개밖에 없어서 크게 유의미한 것 같지는 않음
Slack에 업데이트 유무 알림
- Slack API의 Incoming Webhooks App을 사용해 Slack으로 메시지 전송
- Spack API를 사용하기 위한 방법은 아래 페이지의 "Slack 연동하기" 참고
- 변화 확인 : 3개 중 하나라도 변화했다면, 변화한 것으로 판단
- 지원 상태가 "지원확정"에서 변경
- 매칭 상태가 "서류검토중"에서 변경
- 면접 일자가 ""에서 변경
스크래핑 코드 작성
Import
- 사용할 모듈을 import
- time : 빠른 시간의 요청을 피하기 위해 sleep 사용
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
import pandas as pd
import requests
from datetime import datetime
ICT 인턴십 페이지의 데이터 가져오기
설명을 위해 두 개의 코드 블록으로 나눴지만, 하나의 함수로 동작하는 코드이다.
- Selenium을 사용하기 위해 driver 호출
- 추출할 데이터
- company : 회사 이름
- apply_status : 지원 상태
- matching_status : 매칭 상태
- interview_date : 면접 일자
- 인자로 받은 id와 pw를 활용해 로그인 및 알림창 제거
- 로그인 : ID를 통해 각 input 위치를 찾고 'keys.ENTER'로 로그인 진행
- 알림창 제거 : driver.switch_to.alert.accept() 메서드로 제거
def scraping_ictintern(id, pw):
options = webdriver.ChromeOptions()
options.add_argument("--headless")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
company, apply_status, matching_status, interview_date = [], [], [], []
driver.get(f"https://www.ictintern.or.kr/")
driver.implicitly_wait(3)
login_input = driver.find_element(By.ID, 'userId')
login_input.send_keys(id)
time.sleep(1)
pw_input = driver.find_element(By.ID, 'userPw')
pw_input.send_keys(pw)
time.sleep(1)
pw_input.send_keys(Keys.ENTER)
driver.implicitly_wait(3)
driver.switch_to.alert.accept()
driver.implicitly_wait(3)
- XPATH로 '인턴십관리' 버튼을 찾고 click() 메서드로 클릭
- 원하는 데이터의 위치를 찾아 반복문으로 데이터 추출 후 반환
manage_button = driver.find_element(By.XPATH, '//*[@id="content-wrap"]/div[1]/div[2]/div[1]/div/span[2]/a/em')
manage_button.click()
company_list = driver.find_elements(By.CLASS_NAME, "tron")
for c in company_list:
info_list = c.find_elements(By.TAG_NAME, 'td')
company.append(info_list[3].text)
apply_status.append(info_list[6].text)
matching_status.append(info_list[7].text)
interview_date.append(info_list[8].text)
driver.quit()
return company, apply_status, matching_status, interview_date
추출한 데이터를 DataFrame 형태로 변환
- 추출한 데이터를 인자로 받아 DataFrame 형태로 반환
def convert_list_to_dataframe(company, apply_status, matching_status, interview_date):
df = pd.DataFrame ({'company' : company,
'apply_status' : apply_status,
'matching_status' : matching_status,
'interview_date' : interview_date})
return df
Slack에 업데이트 유무 알림
- 변화 유무 확인
- 지원 상태가 "지원확정"이 아닌 것이 있는지 확인
- 매칭 상태가 "서류검토중"이 아닌 것이 있는지 확인
- 면접 일자가 ""이 아닌 것이 있는지 확인
- 변화가 없다면, "변화가 없습니다!" 메시지 전송
def check_any_change_in_df(ict_intern_info):
ict_intern_info['combined'] = ict_intern_info.apply(lambda row: ' '.join(row.astype(str)), axis = 1)
data_msg = '\n'.join(ict_intern_info['combined'].tolist())
if (ict_intern_info['apply_status'] != '지원확정').any():
send_message_on_slack(f"일정을 확인해보세요!\n{data_msg}")
elif (ict_intern_info['matching_status'] != '서류검토중').any():
send_message_on_slack(f"일정을 확인해보세요!\n{data_msg}")
elif (ict_intern_info['interview_date'] != '').any():
send_message_on_slack(f"일정을 확인해보세요!\n{data_msg}")
else:
send_message_on_slack(f"변화가 없습니다!\n{data_msg}")
- Slack API를 통해 얻은 Slack Webhooks URL을 활용해 인자로 받은 메시지를 Slack으로 전송
- 실행한 시간과 받은 메시지를 Slack에서 확인 가능
def send_message_on_slack(msg):
url = 'SLACK HOOK URL'
header = {'Content-type': 'application/json'}
username = "Hook"
attachments = [{
"color": "good",
"text": f"실행 시간 : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n{msg}\n"
}]
data = {"username": username, "attachments": attachments}
return requests.post(url, headers=header, json=data)
__main__
if __name__ == "__main__":
id = 'ID'
pw = 'PW'
company, apply_status, matching_status, interview_date = scraping_ictintern(id, pw)
ict_intern_info = convert_list_to_dataframe(company, apply_status, matching_status, interview_date)
check_any_change_in_df(ict_intern_info)
결과
- Slack에 올바르게 메시지가 전송된 것을 확인
자동화
일정 시간마다 업데이트 유무를 확인하기 위해 Amazon EC2의 Ubuntu 서버를 사용하였다. 그러나 EC2에서 selenium을 활용하는 것이 결코 쉽지는 않았다. 자동화 과정을 살펴보자.
Amazon EC2 인스턴스 생성 및 접속
EC2 생성과 접속 과정은 간단하게 소개하려고 한다.
- 생성 과정
- 운영체제 : Ubuntu Server 24.04 LTS -> Free Tier 사용
- 키페어 생성 및 보안 그룹 설정
- 접속 과정
- 아래의 코드를 Shell에 입력하여 접속
- 만약 접속되지 않는다면, 보안 > 보안 그룹 > 인바운드 규칙에 22 port(ssh) 추가
ssh -i "선택 or 생성한 KEY 경로" ubuntu@"퍼블릭 IPv4 주소"
Ubuntu 업데이트 및 Selenium 사용 준비
이 과정에서 시간이 오래 걸렸다. Selenium을 EC2에서 사용하는 것은 처음이었는데, 생각보다 쉬운 과정은 아니었다. 다양한 페이지를 참고하며, Selenium을 사용하기 위한 세팅을 진행하였다. 모든 과정이 필수적인지 정확히 확인하지는 않았지만, Selenium은 정상적으로 동작한다.
- Ubuntu 업데이트 및 필수 패키지 설치
# Ubuntu 업데이트
sudo apt update && sudo apt upgrade -y
# 필수 패키지 설치
sudo apt install apt-transport-https ca-certificates curl software-properties-common wget -y
- 구글 크롬 설치
# Google Chrome의 최신 .deb 파일 다운로드
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
# 다운로드 받은 .deb 파일을 설치
sudo dpkg -i google-chrome-stable_current_amd64.deb
# 만약 의존성 문제로 설치가 중단된 경우 의존성 문제를 해결하고 설치를 완료
sudo apt --fix-broken install
- 크롬 버전 확인
google-chrome --version
# Google Chrome 126.0.6478.126
- 크롬 버전에 맞게 크롬 드라이버를 설치 (126 버전 링크)
- 만약 버전에 맞지 않으면 Selenium이 동작하지 않음 -> 알고 싶지 않았다..ㅠㅠ
# 크롬 드라이버 압축 파일 가져오기
wget https://storage.googleapis.com/chrome-for-testing-public/126.0.6478.126/linux64/chromedriver-linux64.zip
# 크롬 드라이버 압축 해제
unzip chromedriver-linux64.zip
# 압축 파일 삭제
rm chromedriver-linux64.zip
Python 사용 준비
Selenium 준비는 끝났다! 그러면 이제 Python 파일을 실행할 수 있도록 하는 작업을 해볼 것이다.
- python3 및 python3-pip 설치
sudo apt install python3
sudo apt install python3-pip
- externally-managed-environment 에러 발생 시 (링크)
해결 방법 찾으신 분 매우 감사합니다..
python3 -m pip config set global.break-system-packages true
- 사용할 패키지 설치
pip install selenium
pip install pandas
pip install requests
pip install time
스크래핑 코드 수정
드디어 Ubuntu에서 코드를 실행할 준비가 끝났다. 그런데 Ubuntu(Linux) 환경에서 Selenium을 실행하기 위해서는 위에서 사용한 driver를 수정해주어야 한다. 바로 진행해 보자!!!
- 원래 코드
def scraping_ictintern(id, pw):
options = webdriver.ChromeOptions()
options.add_argument("--headless")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
- 수정한 코드
- 이전에 압축 해제 진행한 크롬 드라이버의 경로를 지정
- Linux 환경에서 필요한 option 추가
from selenium.webdriver.chrome.options import Options
def scraping_ictintern(id, pw):
service = Service(executable_path='/home/ubuntu/chromedriver-linux64/chromedriver')
chrome_options = Options()
chrome_options.add_argument("--headless")
# linux 환경에서 필요한 option
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(service=service, options=chrome_options)
Crontab 설정
- crontab 설정 페이지 접속
crontab -e
- 매 1시간마다 실행되도록 설정
0 */1 * * * python3 scraping.py
결과
이제 면접 준비를 하면서 서류 결과와 면접 일정을 확인하면 될 것 같다!!
- 매 1시간마다 실행되는 것을 확인
Reference
https://suucong.tistory.com/58
https://velog.io/@jay2u8809/Crontab%ED%81%AC%EB%A1%A0%ED%83%AD-%EC%8B%9C%EA%B0%84-%EC%84%A4%EC%A0%95
https://amanokaze.github.io/blog/PIP-Installation-in-EC2-Instance/#google_vignette