[Web Scraping 기초] Selenium으로 웹브라우저 자동화하기
Selenium 라이브러리
- selenium은 Python을 이용해서 웹 어플리케이션 자동화 및 테스트를 위한 포터블 프레임워크이다
1) selenium 및 webdriver 설치
- selenium을 사용하기 위해 먼저 selenium 프레임워크를 설치해야한다
- pip install을 통해 간단하게 실행할 수 있다 (cmd창에 아래 코드 입력, Jupyter Notebook에선 맨 앞에 % 붙임)
- webdriver는 사용자를 대신해서 웹 브라우저를 움직이도록 하는 모듈이다
- selenium 4.0 이전 까지는 필요에 따라 각각의 웹 드라이버를 따로 설치해야 했으나(Chrome 사용 시 Chrome webdriver
를 설치) selenium 4.0 부터는 'webdriver-manager'만 설치해주면 된다
pip install selenium
pip install webdriver-manager
2) Selenium 시작하기
ⓛ selenium을 사용하기 위해선 관련된 모듈들을 불러와야 한다
from selenium import webdriver # web browser와 직접적인 연결
from selenium.webdriver.edge.service import Service # Edge 객체 생성을 위한 모듈
# Chrome : from selenium.webdriver.chrome.service import Service # 지금 사용하는 웹브라우저 버전과 싱크
from webdriver_manager.microsoft import EdgeChromiumDriverManager
# Chrome : from webdriver_manager.chrome import ChromeDriverManager
② 불러온 모듈 webdriver에서 Edge 객체를 생성
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
# Chrome : driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# 이하 과정은 Chrome과 Edge 동일
③ 웹 드라이버를 통해 get 요청을 보내고 응답을 받은 후, page_source 속성을 통해 HTML 문서를 확인
driver.get("http://www.example.com")
print(driver.page_source)
④ 특정 요소 추출하기
- 생성한 객체를 통해 필요한 특정 요소를 추출할 수 있다
- 이 명령을 사용하기 위해선 By 모듈이 필요하다
- 단수 : find.element(by, target) / 복수 : ind.elements(by, target)
1. by : 찾는 대상(id → ID, <tag> → TAG_NAME, class → CLASS_NAME ...)
2. taget : 대상의 속성
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.common.by import By #By 모듈 import
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
driver.get("http://www.example.com")
print(driver.page_source)
# 단수
with webdriver.Edge(service=Service(EdgeChromiumDriverManager().install())) as driver:
driver.get("http://www.example.com")
print(driver.find_elemnet(By.TAG_NAME, "p").text)
#복수
with webdriver.Edge(service=Service(EdgeChromiumDriverManager().install())) as driver:
driver.get("http://www.example.com")
# print(driver.find_element(By.TAG_NAME, "p").text)
for element in driver.find_elements(By.TAG_NAME, "p") : # driver.find_elements(By.TAG_NAME, "p") 실행 후 list return
print(element.text)
Wait and Call
1) 동적 웹사이트의 동작 방식
- HTML 문서가 완전하게 응답되는 정적 웹 사이트와 달리 동적 웹사이트에선 JavaScript라는 프로그래밍 언어가
동작한
- 동적 웹사이트는 비동기 처리와 동기 처리로 데이터를 처리한다
1. 동기 처리
- 렌더링이 끝난 후 데이터 처리가 시작된다 (요청에 따른 응답을 기다린다)
- 동기 처리가 완료되면 HTML 로딩에 문제가 없다
2. 비동기 처리
- 렌더링 완료와 상관 없이 데이텉 처리가 시작된다 (요청에 따른 응답을 기다리지 않는다)
- 비동기 처리된 경우, 상황에 따라서 데이터가 완전하지 않은 경우가 발생한다.
- 비동기 처리 웹 사이트에서 불완전한 응답을 받은 상태에서 스크래핑을 진행할 경우 오류가 발생할 수 있다
- 따라서, 데이터 처리가 완료되기 까지 기다려 줘야(Wait) 한다
2) XPath
- 웹 스크래핑이 사용이 늘면서 요즘 웹 사이트는 스크래핑을 방지할 목적으로 랜덤하게 class 이름을 생성한다
- 이러한 경우에서 쓸 수 있는 방법 중 하나가 XPath 이다
- XPath은 원하는 요소의 '위치'를 경로로 표현하는 것이다.
- XPath는 해당 요소 '우클릭 → 복사 → XPath 클릭' 으로 XPath를 추출할 수 있다
3) Implicit wait / Explicit Wait
①Implicit wait (암시적 기다림)
- 암시적 기다림은 사용자가 지정한 시간 내 데이터 처리가 완료되면 지정한 시간과 상관 없이 알아서(암시적)
다음 명령어를 실행시는 방식이다
- .implicitly_wait()을 활용해서 암시적 기다림을 적용할 수 있다
* 예시 사이트 : https://indistreet.com/live?sortOption=startDate%3AASC
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.support.ui import WebDriverWait
# driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
# driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
# print(driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]').text)
with webdriver.Edge(service=Service(EdgeChromiumDriverManager().install())) as driver :
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(10) # 10초를 기다리는데 그 중 응답이 다 오면 바로 다음 명령문 실행
print(driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]').text)
② Explicit Wait (명시적 기다림)
- 명시적 기다림은 사용자가 지정한 시간 내 (사용자가 명시한) target인 요소가 존재할 때까지 기다린 후 다음 명령을
실행하는 방식이다
- WebDriverWati()와 추가로 두 메소드를 활용해서 명시적 기다림을 적용할 수 있다
1. until() : 인자 조건이 만족될 때까지 (원하는 요소가 나타날 때까지)
2. until_not() : 인자의 조건이 만족되지 않을 때까지
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.support.ui import WebDriverWait
with webdriver.Edge(service=Service(EdgeChromiumDriverManager().install())) as driver :
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]')))
print(element.text)
마우스 및 키보드 이벤트 처리하기
1) 키보드 이벤트 처리하기
- 웹 페이지에서 일어나는 일들을 Event 라고 하며, 마우스로 일어날 수 있는 대표적인 이벤트는 마우스 움직이기(move),
누르기(press down), 떼기(press up) 이다
- 마우스 입력에는 크게 다음과 같은 과정이 거친다
1. 입력하고자 하는 대상 요소를 찾기 → 2. 입력하고자 하는 내용 click() 메소드를 통해 전달 → 3. perform()을 통해 동작
* 예시 사이트 : https://hashcode.co.kr
# hashcode.co.kr에서 로그인 버튼 누르기 자동화
from selenium.webdriver import ActionChains # 마우스 이벤트를 위한 모듈
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.microsoft import EdgeChromiumDriverManager
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
driver.get("https://hashcode.co.kr/")
driver.maximize_window() # XPath에 맞는 화면 크기 조절
driver.implicitly_wait(3)
button = driver.find_element(By.XPATH, '//*[@id="main-app-gnb-header"]/div/div/div[1]/div/div[1]/a')
ActionChains(driver).click(button).perform
※ 화면 크기에 따라 XPath가 달라질 수 있다. 원하는 요소를 찾지 못했다는 오류가 뜬다면 XPath를 어떤 화면에서
추출했는지 확인해봐야 한다. 만약, 창 크기를 많이 줄여 모바일 화면으로 변한 사이트에서 추출한 XPath와 일반적인
PC화면에서 추출한 XPath는 서로 다르다.
2) 키보드 이벤트 처리하기
- 키보드로 일어날 수 있는 대표적인 이벤트는 키보드 누르기(press down), 키보드 떼기(press up) 이다
- 원하는 요소에 키보드 입력을 진행하는 과정은 크게 다음과 같은 과정이 거친다
1. 입력하고자 하는 대상 요소를 찾기 → 2. 입력하고자 하는 내용 send_keys_to_element() 메소드를 통해 전달
→ 3. perform()을 통해 동작
* 예시 사이트 : https://hashcode.co.kr
# 마우스 이벤트 처리와 더불어 키보드 이벤트 처리로 로그인 과정 자동화
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver import Keys, ActionChains
# driver를 이용해 해당 사이트에 요청을 보내기
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
driver.get("https://hashcode.co.kr/")
# 내비게이션 바에서 "로그인" 버튼을 클릭
driver.maximize_window()
driver.implicitly_wait(3)
button = driver.find_element(By.XPATH, '//*[@id="main-app-gnb-header"]/div/div/div[1]/div/div[2]/div/div/div[1]/span[1]/a[1]')
ActionChains(driver).click(button).perform()
# "아이디" input 요소에 여러분의 아이디를 입력
id_input = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/div[2]/input')
ActionChains(driver).send_keys_to_element(id_input, "이메일").perform()
# "패스워드" input 요소에 여러분의 비밀번호를 입력
pw_input = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/div[4]/input')
ActionChains(driver).send_keys_to_element(pw_input, "패스워드").perform()
# "로그인" 버튼 클릭
login_button = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/button')
ActionChains(driver).click(login_button).perform()