requests로 백준 로그인 구현하기
백준에 제출한 코드를 열람하기 위해서는 반드시 로그인이 되어 있어야 하고, 비공개로 설정된 코드는 제출한 계정으로만 열람이 가능합니다.
저는 가장 많이 사용되는 requests 라이브러리를 활용하여 로그인을 구현해보기로 결정했습니다.
먼저, 간단하게 사용자 아이디, 비밀번호 정보를 통해 로그인 함수를 구현해보았습니다.
import requests
BASE_URL = "https://www.acmicpc.net"
def login(boj_id, boj_pwd):
session = requests.Session()
login_url = f"{BASE_URL}/signin"
payload = {'login_user_id': boj_id,
'login_password': boj_pwd}
res = session.post(url=login_url, data=payload)
print("res:", res)
print("res.status_code:", res.status_code)
호기롭게 코드를 작성하고 실행해봤지만, 결과는 403 에러였습니다😰
검색해보니, 403 에러는 해당 웹사이트에서 접속을 거절한 경우에 발생하는 에러라고 합니다. 구글링 결과, headers를 통해 Referer를 지정해주면 해결된다고 하여 아래를 추가해주었습니다.
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.acmicpc.net/"
}
res = session.post(url=login_url, data=payload, headers=headers) # 변경
결과는 200 성공이었습니다🎉 전체 코드는 아래 코드와 같습니다.
import requests
BASE_URL = "https://www.acmicpc.net"
def login(boj_id, boj_pwd):
session = requests.Session()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.acmicpc.net/"
}
login_url = f"{BASE_URL}/signin"
payload = {'login_user_id': boj_id,
'login_password': boj_pwd}
res = session.post(url=login_url, data=payload, headers=headers)
print("res:", res)
print("res.status_code:", res.status_code)
requests로 제출 기록 조회하기
로그인이 성공했다면, 이 로그인 세션을 유지하면서 사용자의 제출 기록을 백준 사이트에서 가져와야 합니다. 마찬가지로 requests를 이용해 get 요청을 보내 정보를 가져오도록 했고, 로그인 세션이 유지어야 하므로 session과 header를 전역으로 빼주었습니다.
session = requests.Session()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.acmicpc.net/"
}
def getStatus(boj_id):
status_url = f"{BASE_URL}/status?user_id={boj_id}&result_id=4"
res = session.get(status_url, headers=headers)
print("res", res)
print("res.status_code", res.status_code)
print("res.text", res.text)
if res.status_code == 200:
print(boj_id, "조회 성공")
return res.text
else:
print(res.status_code, "조회 실패")
return False
조회에 성공하면, 아래 이미지와 같이 아주 긴 html 응답을 확인할 수 있습니다.
BeautifulSoup으로 HTML 파싱하기
제출 기록을 html 응답으로 받아왔다면, 이제 이를 파싱하여 필요한 코드만 추출해낼 차례입니다. 저는 BeautifulSoup을 이용해 HTML 파싱을 구현하였습니다.
우선 제가 가져올 정보가 어디있는지를 알아내기 위해 웹사이트에서 개발자 모드를 켜서 직접 확인해보았습니다.
html을 살펴보면, table 안에 각 tr마다 제출 목록이 들어있는 걸 확인해볼 수 있음을 발견했습니다. 그러면, 이 정보를 코드를 통해 추출해봅시다!
def getInfo(cols):
submission_id = cols[0].text.strip() # 제출 ID
user_id = cols[1].find("a").text.strip() # 사용자 아이디
problem_id = cols[2].find("a").text.strip() # 문제 번호
problem_title = cols[2].find("a").get("title", "").strip() # 문제 제목
result = cols[3].find("span").text.strip() # 채점 결과
memory = cols[4].text.strip() + "KB" # 메모리 사용량
time = cols[5].text.strip() + "ms" # 실행 시간
language = cols[6].text.strip() # 사용 언어
code_length = cols[7].text.strip() + "B" # 코드 길이
return {"submission_id": submission_id,
"user_id": user_id,
"problem_id": problem_id,
"problem_title": problem_title,
"result": result,
"memory": memory,
"time": time,
"language": language,
"code_length": code_length,
}
def extractSubmission(text):
soup = BeautifulSoup(text, "html.parser")
submission_list = {}
for row in soup.select("tr"):
cols = row.find_all("td")
if len(cols) == 0: continue
info = getInfo(cols)
submission_list[info["problem_id"]] = info
return submission_list
전체 코드
완성된 전체 코드는 다음과 같습니다!
import requests
from bs4 import BeautifulSoup
BASE_URL = "https://www.acmicpc.net"
session = requests.Session()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"Referer": "https://www.acmicpc.net/"
}
def login(boj_id, boj_pwd):
login_url = f"{BASE_URL}/signin"
payload = {'login_user_id': boj_id,
'login_password': boj_pwd}
res = session.post(url=login_url, data=payload, headers=headers)
if res.status_code == 200:
print(boj_id, "로그인 성공")
return True
else:
print(res.status_code, "로그인 실패")
return False
def getStatus(boj_id):
status_url = f"{BASE_URL}/status?user_id={boj_id}&result_id=4"
res = session.get(status_url, headers=headers)
if res.status_code == 200:
print(boj_id, "조회 성공")
return res.text
else:
print(res.status_code, "조회 실패")
return None
def getInfo(cols):
submission_id = cols[0].text.strip() # 제출 ID
user_id = cols[1].find("a").text.strip() # 사용자 아이디
problem_id = cols[2].find("a").text.strip() # 문제 번호
problem_title = cols[2].find("a").get("title", "").strip() # 문제 제목
result = cols[3].find("span").text.strip() # 채점 결과
memory = cols[4].text.strip() + "KB" # 메모리 사용량
time = cols[5].text.strip() + "ms" # 실행 시간
language = cols[6].text.strip() # 사용 언어
code_length = cols[7].text.strip() + "B" # 코드 길이
return {"submission_id": submission_id,
"user_id": user_id,
"problem_id": problem_id,
"problem_title": problem_title,
"result": result,
"memory": memory,
"time": time,
"language": language,
"code_length": code_length,
}
def extractSubmission(text):
soup = BeautifulSoup(text, "html.parser")
submission_list = {}
for row in soup.select("tr"):
cols = row.find_all("td")
if len(cols) == 0: continue
info = getInfo(cols)
submission_list[info["problem_id"]] = info
return submission_list
if __name__ == '__main__':
import os
from dotenv import load_dotenv
load_dotenv()
boj_id = os.getenv("BOJ_ID")
boj_pwd = os.getenv("BOJ_PWD")
if login(boj_id, boj_pwd):
text = getStatus(boj_id)
extractSubmission(text)
'백엔드' 카테고리의 다른 글
[BOJ-AutoSync] 4. Github OAuth Apps 연동하기 (0) | 2025.03.16 |
---|---|
[BOJ-AutoSync] 3. FastAPI로 BOJ 조회 API 개발하기 (0) | 2025.03.16 |
[BOJ-AutoSync] 2. selenium으로 BOJ 로그인하고 코드 불러오기 (0) | 2025.03.14 |
[BOJ-AutoSync] 0. 프로젝트 기획 (0) | 2025.03.14 |