식혜야 2024. 1. 24. 22:11

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)

 

마우스 이벤트

  1. 입력하고자 하는 대상 요소 찾기 (find_element() 이용)
  2. 입력하고자 하는 내용을 click을 통해 전달
  3. .perform()을 통해 동작

이 외의 마우스 이벤트 링크

키보드 이벤트

  1. 입력하고자 하는 대상 요소 찾기 (find_element() 이용)
  2. 입력하고자 하는 내용을 send_keys_to_element를 통해 전달
  3. .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)