회사 업무를 위해 Selenium을 열심히 사용하다가 문득 이런 생각이 들었다.
"셀레니움으로 로그인만 하고 해당 인증 정보로 requests 라이브러리를 사용해 HTTP 요청을 할 순 없을까?"
사용자 정보 수정을 하기 위해 메뉴를 타고 들어가는 과정을 셀레니움으로 자동화하는 것보다 로그인만 셀레니움으로 시도하여 해당 인증 정보로 바로 사용자 정보 수정 HTTP 요청을 해버리면 메뉴를 이동하다가 발생하는 오류를 방지하는데 도움이 될 것 같았다.
selenium의 세션 정보를 requests에서 사용하는 방법은 생각보다 간단하였다.
import requests
from selenium import webdriver
#
# driver 생성 후 페이지 로그인
#
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
# 이후 session.get() / session.post() / session.put() / session.delete() 로 HTTP 요청 사용
이 방법으로 requests에서 HTTP 요청이 정상적으로 수행되는지 테스트해 보자.
지난 번에 말했던 것처럼 "서울도서관 전자도서관"에서 테스트를 해보기로 하였다.
if __name__ == '__main__':
driver = get_chrome_driver()
target_url = 'https://elib.seoul.go.kr/main'
driver.get(target_url)
loading_div = WebDriverWait(driver, 3).until(
EC.presence_of_element_located((By.XPATH, '//div[@class="wrap-loading"]'))
)
while loading_div.is_displayed():
time.sleep(0.5)
"WAIT"
driver.find_element(By.XPATH, '//li[@class="in_logout"]/a').click()
id_input = WebDriverWait(driver, 3).until(
EC.presence_of_element_located((By.XPATH, '//input[@id="id"]'))
)
id_input.send_keys('{서울도서관 ID}')
driver.find_element(By.XPATH, '//input[@id="password"]').send_keys('{서울도서관 PASSWORD}')
driver.find_element(By.XPATH, '//input[@type="submit"]').click()
driver.quit()
빠르게 selenium을 사용하여 서울도서관 전자도서관에 로그인을 하는 스크립트를 만든 뒤, requests로 HTTP 요청을 테스트하기 위해 도서 대출 시 어떤 형식으로 HTTP 요청을 하는지 확인한다.
대상 도서는 "21개의 작고 재미난 파이썬 프로젝트".
Chrome에서 개발자 도구를 열어준 뒤, '네트워크' 탭에 들어간 다음 대출을 눌러주면
위와 같이 어떤 형태로 요청을 하는지 확인할 수 있다.
POST 방식으로, contentKey에는 도서의 키를, loanDevice는 정확히 뭘 의미하는지 모르겠지만 1 값을 넘겨주면 될 것 같다.
loanDate는 대출 요청하는 현재 시간을 YYYY-MM-DD HH:MM:SS 형태로 전달하면 되고, returnPlanDate는 반납일을 YYYY-MM-DD HH:MM:SS 형태로 전달하면 될 것 같다.
loanDate와 returnPlanDate는 datetime 모듈을 사용하여 다음과 같이 값을 가져오기로 한다.
이제 POST 요청을 보내는 파라미터 값을 모두 찾았으니 위의 방법대로 requests를 이용한 HTTP 요청을 날려보기로 한다.
now = datetime.now()
loan_date = now.isoformat(sep=' ', timespec='seconds')
return_date = (datetime(now.year, now.month, now.day, 23, 59, 59) + timedelta(days=14)) \
.isoformat(sep=' ', timespec='seconds')
content_key = 'PRD000143093'
load_device = '1'
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
target_url = 'https://elib.seoul.go.kr/api/detail/loan'
data = {"contentKey": content_key, "loanDevice": load_device, "loanDate": loan_date, "returnPlanDate": return_date}
res = session.post(url=target_url, data=data)
print(res.status_code)
print(res.text)
결과로 404가 반환되었다.
왜인지 다시 대출 시 요청되는 POST의 헤더를 보면 알 수 있다.
바로 헤더에 X-Auth-Token을 넣어서 보내기 때문에 위에서 시도한 요청은 정상적으로 처리되지 않는 것이다.
이 X-Auth-Token으로 전달되는 accessToken의 값은 개발자 도구의 애플리케이션 > 세션 스토리지에서 확인할 수 있다.
이 값을 Selenium의 execute_script로 읽어와 X-Auth-Token으로 헤더에 넣어 POST 요청을 보내기로 하자.
now = datetime.now()
loan_date = now.isoformat(sep=' ', timespec='seconds')
return_date = (datetime(now.year, now.month, now.day, 23, 59, 59) + timedelta(days=14)) \
.isoformat(sep=' ', timespec='seconds')
content_key = 'PRD000143093'
load_device = '1'
# execute_script로 accessToken 값 가져오기
access_token = driver.execute_script('return sessionStorage.getItem("accessToken");')
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
target_url = 'https://elib.seoul.go.kr/api/detail/loan'
data = {"contentKey": content_key, "loanDevice": load_device, "loanDate": loan_date, "returnPlanDate": return_date}
# accessToken을 헤더의 X-Auth-Token 값으로 전달
headers = {"X-Auth-Token": access_token}
res = session.post(url=target_url, data=data, headers=headers)
print(res.status_code)
print(res.text)
accessToken 값을 헤더에 넣어 요청하자 정상적으로 대출이 수행되는 것을 확인할 수 있다.