겨울 방학동안 연합 알고리즘 스터디 동아리 활동을 하게 됐다.
동아리 활동은 알고리즘 강의를 듣고 BOJ에 올린 연습문제를 푸는 것이다.
연습문제 풀이 여부를 통해 출석체크를 한다.
이번에 나는 동아리 멘토의 역할로 일정 동아리원을 담당하여 출석체크를 하는 역할을 맡게 되었다.
그런데 문제점이 있었다.
현재 스터디 구성원이 총 300명 가까이 되는데, 그 중에 내가 담당한 스터디원은 약 30명이다.
그리고 백준 연습문제는 스코어보드 형식이기 때문에, 먼저 문제를 다 푼 사람 순서대로 정렬된다.
백준 연습문제의 300명치 스코어보드를 보고 내가 담당한 30명을 골라내어 일일히 체크한다..?
사실 30명 정도면 그렇게 많은 사람도 아니고,
실제로 300명이 모두 출석을 다하지는 않기 때문에 생각보다 오래 걸리는 일은 아니지만,
실수하기가 매우 쉽기도 하고 불편한 작업인 것은 사실이다.
그래서 문득 이걸 웹 크롤링으로 프로그램에게 시키면 어떨까 하는 생각을 해보았다.
사실 웹 크롤링은 학교에서 특강 형식으로 간단하게 배워본 적이 있었다.
물론 그걸 배울 당시엔 설명을 제대로 이해도 못하고 크롤링을 했었다.
하지만 이제는 html, css도 가볍게 배웠고,
카카오톡 봇을 만들면서 가벼운 크롤링을 해본 경험이 있기 때문에 더 쉽게 할 수 있다고 생각했다.
바로 구글링을 하면서 셀레니움, 뷰티풀수프를 가볍게 공부해서 코딩을 해보았다.
그러던 중 첫번째 문제점을 만났다.
크롬에서 분명 로그인 유지까지 시켜서 로그인 상태로 만들어 뒀는데,
셀레니움으로 크롬창을 열어서 들어가니 로그인이 계속 풀려있는 것이었다.
뭔가 내가 쓰는 크롬과 셀레니움의 크롬창이 다른 것 같아서
구글에서 쉽게 찾을 수 있는 로그인 예제를 활용해보았다.
그러나 여전히 다른 문제점이 남아 있었다.
백준사이트에서 어떻게 감지했는지 몰라도, 셀레니움으로 로그인을 시도하자
자동 로그인 방지를 위해 로봇이 아님을 확인하는 작업을 거친 것이다.
한참을 고민하던 중에 하나의 아이디어가 떠올랐다.
어차피 출석체크는 일주일에 한번만 하면 되고,
보통의 크롤링처럼 계속 주기적으로 정보를 수집해오는 게 아니다.
그냥 내가 미리 백준에 로그인까지 다 해서 연습문제 스코어 창까지 띄워놓고
그 창에서 크롤링을 진행한다면 어떨까
바로 구글링을 통해 정답을 찾을 수 있었다.
https://jakpentest.tistory.com/39
이 정보를 토대로 미리 스코어보드 사이트 창을 띄운채로, 다음과 같이 코딩했다.
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
from bs4 import BeautifulSoup
import time
import pandas as pd
import csv
columns = [str(x) for x in range(1,12)]
check_id = [아이디 목록 리스트 : 데이터 프레임의 인덱스 역할을 한다.]
dataFrame = pd.DataFrame(' ', index=check_id, columns = columns)
#dataFrame = pd.read_csv("./attend_check.csv", index_col=0, header=0)
print(dataFrame)
chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Chrome("C:/Users/kckc0/Desktop/chromedriver.exe", options=chrome_options)
#driver.get('https://www.acmicpc.net/group/연습문제 주소')
#driver.refresh()
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
index = 1
''' 진행 주차 반드시 수정 '''
week = '1'
''' 진행 주차 반드시 수정 '''
while True:
selecter_id = '#contest_scoreboard > tbody > tr:nth-child({index}) > th:nth-child(2) > a'.format(index = index)
selecter_num = '#contest_scoreboard > tbody > tr:nth-child({index}) > td:nth-child(8)'.format(index = index)
user_id = soup.select(selecter_id)[0].text
num_solve = int(str(soup.select(selecter_num)[0].text).split()[0])
time.sleep(0.1)
print(user_id, num_solve)
index += 1
if num_solve < 5:
break
if user_id in check_id:
dataFrame[week][user_id] = 'O'
print(dataFrame)
dataFrame.to_csv("./attend_check.csv")
알고리즘은 간단하다.
내가 담당한 사람들의 백준 아이디를 모아서 미리 리스트로 만들어두고,
그 리스트를 인덱스로, 1~11주차까지를 헤더로 하는 데이터프레임을 만든다.
그리고 백준 사이트의 연습문제 창에서 유저 아이디와, 맨 우측 풀이한 문제 수를 가져온다.
1주차 기준으로는 5문제를 풀면 출석 인정이 되기 때문에
5문제 미만 푼 사람이 나올 때까지 정보를 받아오면 된다.
(경험상 스코어보드에 올라온 모든 사람이 출석 문제를 다 푼 적은 없었기 때문에
코드에서 예외가 발생할 일은 없을 것이라고 생각한다 ㅋㅋ)
5문제를 푼 사람의 정보를 받아왔을 때, 그 유저의 아이디가 내가 담당한 사람의 아이디라면
그 아이디 인덱스에 맞는 1주차 컬럼에 O 표시를 한다.
그리고 그렇게 만든 데이터프레임을 csv파일로 만들어서 추출하면 끝.
2주차부터는 기존 csv파일을 토대로 데이터프레임을 만들어와서
그 데이터프레임을 수정하면 된다.
그렇게 추출한 csv파일 사진이다.
가운데 정렬이 하고싶어지는 사진이지만ㅋㅋㅋ
어차피 1주차 정보를 그대로 복사해서, 동아리 출석 관리 엑셀 폼에 붙여넣기만 하면 되므로?
디테일은 신경쓰지 않기로 했다.
일주일에 한번씩만 돌려서 사이트에 문제 안생기게 조심히 다룰 예정이다..
'개인 프로젝트 > 간단한 프로젝트' 카테고리의 다른 글
[개인프로젝트] 수원시 공고알림 크롤링 및 메일링 프로그램 제작 (0) | 2024.02.10 |
---|---|
[개인프로젝트] tkinter로 GUI 입힌 tcp/ip 소켓 통신 프로그램 만들기 (3) | 2020.08.07 |