TIL_day9 Selenium
Selenium
정적 웹사이트와 동적 웹사이트
정적(static) 웹사이트 : HTML 내용이 고정 ⇒ HTML 문서가 완전하게 응답됨
동적(dynamic) 웹사이트 : HTML 내용이 변함 ⇒ 응답 후 HTML이 렌더링 될 때까지 지연 시간 존재!
동적 웹 사이트의 동작 방식
웹 브라우저에서 동작하는 JS는 비동기 처리를 통해서 데이터를 채운다.
동기 처리: 요청에 따른 응답을 기다린다.
비동기 처리: 요청에 따른 응답을 기다리지 않는다. ⇒ 상황에 따라 데이터가 완전하지 않을 수 있음
- 비동기 처리로 인해 불완전한 응답받게 됨 = 응답 후 바로 정보를 추출하기 어렵다.
- 키보드 입력, 마우스 클릭 등 UI 상호작용은 requests로는 진행하기 어려움
- 임의로 시간을 지연해 데이터 처리가 끝난 후 정보를 가져오기를 통해 해결
⇒ Selenium 사용
Selenium이란?
Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크. 오픈소스.
스크래핑 시, 웹 브라우저와의 연동을 위해서는 WebDriver가 필요.
WebDriver란? 웹 브라우저를 제어할 수 있는 자동화 프레임워크.
셀레니움 크롬드라이버 설치(버전 이슈)
24.01.21 파이썬의 selenium을 사용하려면 스크래핑할 웹 브라우저인 크롬의 크롬 드라이버를 설치해줘야 한다. 이때 내가 실제로 사용하는 크롬 브라우저의 버전과 내가 설치하여 사용할 크롬 드라
sikhyekim.tistory.com
Selenium 사용법
# selenium 라이브러리 설치
%pip install selenium
# webdriver 관리하는 라이브러리 설치
%pip install webdriver-manager
# selenium으로부터 webdriver 모듈을 불러옵니다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import requests
# webdriver에서 Chrome() 객체 생성
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
### 크롬이 실행됨 ###
# http://www.example.com 으로 요청을 보내봅시다.
driver.get("http://www.example.com")
# page_source 속성을 통해 Response의 HTML문서 확인.
print(driver.page_source)
# with-as를 사용해서 주어진 명령이 끝나면 driver를 종료하도록 설정.
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("http://www.example.com")
print(driver.page_source)
요소 추출
- .find_element(by, target)
- .find_elements(by, target)
- by : 대상을 찾는 기준 - ID, TAG_NAME, CLASS_NAME …
- target: 대상의 속성
# By를 import 해봅시다.
from selenium.webdriver.common.by import By
# p 태그에 해당하는 요소 하나를 찾아봅시다.
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("http://www.example.com")
print(driver.find_element(By.TAG_NAME, "p").text)
# p 태그에 해당하는 요소 여러개를 찾아봅시다.
with webdriver.Chrome(path) as driver:
driver.get("http://www.example.com")
for element in driver.find_elements(By.TAG_NAME, "p"):
print("Text: ", element.text)
암묵적 기다림(Implicit Wait)과 명시적 기다림(Explicit Wait)
- Implicit Wait
- 특정 요소에 대한 제약을 통한 기다림 (e.g. 이 태그를 가져올 수 있을 때까지 기다려!)
- 반드시 해당 시간을 기다리는 것이 아니라, 로딩이 다 될 때까지의 한계 시간을 의미
- Explicit Wait
- Implicit Wait를 사용해 웹페이지는 열렸지만, JS로 만들어진 웹페이지의 일부는 렌더링이 안 되었을 수 있다. 그래서 Explicit Wait을 사용해 요소를 지정해 준다.
- 다 로딩이 될 때까지 지정한 시간 동안 기다림 (e.g. 다 로딩이 될 때까지 5초 동안 기다려!)
XPath란, XML, HTML 문서 등의 요소의 위치를 경로로 표현하는 것.
예제 - 사이트
# 스크래핑에 필요한 라이브러리를 불러와봅시다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
# 예시 사이트에 요청을 진행하고, 예시 사이트의 첫 번째 이벤트의 제목을 가져와봅시다.
# 오류 발생!!!!!!!!!!! - 동적 웹페이지이기 때문
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
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
=> 오류 해결하기 위해 wait 걸어줌.
방법 1 - Implicit Wait
# Implicit Wait
from selenium.webdriver.support.ui import WebDriverWait
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(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)
방법 2 - Explicit Wait
- until(): 인자의 조건이 만족될 때까지
- until_not(): 인자의 조건이 만족되지 않을 때까지
- EC는 expected_conditions
selenium.webdriver.support.expected_conditions — Selenium 4.17.2 documentation
An expectation for checking that an element, known to be present on the DOM of a page, is visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0. element is the WebElement returns the (same)
www.selenium.dev
# Explicit Wait
from selenium.webdriver.support import expected_conditions as EC
with webdriver.Chrome(service=Service(ChromeDriverManager().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)
# 여러 공연(10개)의 제목을 스크래핑하는 코드를 작성해봅시다.
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(10)
#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]')))
for i in range(1, 11):
element = driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[{}]/div/a/div[2]/p[1]'.format(i))
print(element.text)
마우스 이벤트
- 입력하고자 하는 대상 요소 찾기 (find_element() 이용)
- 입력하고자 하는 내용을 click을 통해 전달
- .perform()을 통해 동작
키보드 이벤트
- 입력하고자 하는 대상 요소 찾기 (find_element() 이용)
- 입력하고자 하는 내용을 send_keys_to_element를 통해 전달
- .perform()을 통해 동작
마우스와 키보드 입력 예제 - 사이트
from selenium import webdriver
from selenium.webdriver import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
# driver를 이용해 해당 사이트에 요청을 보내봅시다.
# Implicit Wait은 실행되면 더 이상 기다리지 않고 다음으로 넘어가지만,
# time.sleep은 무조건 명시된 시간 기다림
import time
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://hashcode.co.kr/")
time.sleep(1)
# 내비게이션 바에서 "로그인" 버튼을 찾아 눌러봅시다.
button = driver.find_element(By.CLASS_NAME, "UtilMenustyle__Link-sc-2sjysx-4.ewJwEL")
ActionChains(driver).click(button).perform()
time.sleep(1)
# "아이디"
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, "본인 ID").perform()
time.sleep(1)
# "패스워드"
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, "본인 PW").perform()
time.sleep(1)
# "로그인"
login_button = driver.find_element(By.CLASS_NAME, "itAWTII94uCyf9uUgREi")
ActionChains(driver).click(login_button).perform()
time.sleep(1)