dev/업무자동화

python 업무 자동화 - 동적 크롤링(selenium) 1

이번에는 한투 교육중 가장 프로젝트 점유율이 높았던 크롤링, 그 중에서도 selenium을 활용한 동적 크롤링에 대하여 진행한 부분에 대하여 포스팅 해보려고 한다.

케이스 바이 케이스로 필요한 정보나 사이트들은 달랐지만, 큰 틀에서의 목표는 비슷했다.

기존에 타증권사 금리 조건, 이벤트 현황 수집 혹은 dart,krx 등 공시사이트의 특정 보고서나 뉴스를 주기적으로 한땀 한땀 수기로 집계하시는 것을 자동화 하는것이 목표였다.


>> 동적 크롤링을 선택할 상황

지금 수준에서 requests와 bs4를 활용한 정적 크롤링으로 하기 껄끄럽거나 불가능한, 다음과 같은 상황에서는 오히려 동적 크롤링이 유리하다고 판단하였다.

1. 원하는 사이트의 주소로 한번에 접근이 어려움
2. 상호작용을 통해서만 데이터를 볼 수 있음
3. 구독형 서비스등 경우 로그인을 해야 데이터를 볼 수 있는 경우

>> 목표

1. 원하는 페이지에 로그인 해보기

1번의 경우 홈페이지의 보안 상태에 따라 여러 상황이 있을텐데(분당 접근 횟수 제한, 캡챠등), 보통의 경우 네이버 로그인을 성공할 정도의 변주면 교육기간동안 거진 해결이 되었어서 네이버 로그인( 크롬 )을 기준으로 진행하여 따로 교안을 제작한 기억이 있다.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 웹 드라이버 초기화
driver = webdriver.Chrome()
driver.get('https://www.naver.com')
driver.maximize_window()

# 검색창의 XPath를 사용하여 검색어 입력
driver.find_element(By.XPATH, '//*[@id="account"]/div/a').click()
wait = WebDriverWait(driver, 5)  # 최대 5초간 대기
wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="id"]')))
driver.find_element(By.XPATH, '//*[@id="id"]').send_keys('id')
driver.find_element(By.XPATH, '//*[@id="pw"]').send_keys('pw')
driver.find_element(By.XPATH, '//*[@id="log.login"]').click()

다음과 같은 코드를 통해 로그인 시도를 할 수 있는데, 여기서 아래 사진과 같은 문제가 생긴다.

>> 가설과 시도, 결과

- 가설

1. 너무 빠르게 입력했기 때문에 봇으로 판단 했을 수 있다.
2. 알수없는 감지 시스템이 존재한다.

- 시도

1. 한글자씩 천천히 입력시켜봄 > 실패
2. selenium-stealth등 감지 우회를 위해 개발된 라이브러리 사용 > 실패
3. 클립보드를 이용하는 방식으로 키보드 조작을 시키는 경우 > 성공

 

일단 성공은 했지만, 근본적인 이유를 알지는 못했기 때문에 구글링을 열심히 해보았는데, 아쉽게도 원인을 찾지는 못했다. 다른 방법으로 requests를 이용하는 방법에 대해서는 이론을 잘 설명해주신 블로그를 찾긴 했는데.. 이걸 교육생 분들께 납득시키기엔 어려울 것 같아서 플러스 알파로 링크만 드리고 우선 해결하자 라는 마인드로 접근했다.

-완성코드

import pyperclip
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 웹 드라이버 초기화
driver = webdriver.Chrome()
driver.get('https://www.naver.com')
driver.maximize_window()

# 검색창의 XPath를 사용하여 검색어 입력
elem_account = driver.find_element(By.XPATH, '//*[@id="account"]/div/a')
elem_account.click()

# 대기 후 아이디와 패스워드를 클립보드에 복사
wait = WebDriverWait(driver, 5)
elem_id = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="id"]')))
elem_pw = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="pw"]')))

pyperclip.copy('id')
elem_id.send_keys(Keys.CONTROL, 'v')

pyperclip.copy('pw')
elem_pw.send_keys(Keys.CONTROL, 'v')

# 로그인 버튼 클릭
elem_login = driver.find_element(By.XPATH, '//*[@id="log.login"]')
elem_login.click()

 

이렇게 티켓팅, 구독형 사이트등에 대부분 적용되었던 셀레니움으로 로그인 해보기 마치고, 다음 포스팅에서 데이터 수집시에 쉽고 깔끔하게 되지 않거나 반응형으로 데이터들이 나올때 대처했던 몇 가지 패턴에 대해 기록했던 것을 정리해보아야겠다.