
파이썬으로 웹데이터 크롤하고 분석하기 (4)
Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크
웹 브라우저와 연동하기 위해서는 Webdriver가 필요하다. 실습에서는 Chrome을 사용하였다.
실습에 이용된 사이트는 http://www.example.com 이다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager # 현재 사용하고 있는 크롬의 버전과 동일한 버전을 싱크
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
위의 코드를 실행하면 다음과같이 크롬 창이 새로 열린다.
셀레니움도 requests처럼 .get()을 통해 요청을 보낼 수 있다.
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, find_elements 를 이용해 특정 요소를 찾을 수 있다.
.find_element(by, target).find_elements(by, target)by : 대상을 찾는 기준 : ID, TAG_NAME, CLASS_NAME, ...
target : 대상의 속성
<p> 태그에 해당하는 요소 하나를 찾아보자
from selenium.webdriver.common.by import By # by 이용을 위해
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> 태그에 해당하는 요소 여러개를 찾으려면 find_elements를 쓰면 된다. 단, 여러개를 찾을 때 결과값이 리스트이기 때문에 .text를 쓸 수 없으니 코드를 조정해줘야 한다.
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get('http://www.example.com')
for element in driver.find_elements(By.TAG_NAME, 'p'):
print(element.text)
앞 선 강의에서 동적 웹페이지에 대해 배웠다. 동적 웹페이지의 경우 비동기 처리 때문에 데이터가 로딩되기 전에 find를 진행해 찾고자 하는 element가 없다고 처리되는 경우가 있다. 이 때, 이 페이지를 성공적으로 스크래핑하기 위해서는 Wait을 사용해야한다.
Selenium은 동적 웹 사이트에 대한 지원을 진행하기 위해 명시적 기다림(Explicit Wait)과 암묵적 기다림(Implicit Wait)을 지원한다.
XML, HTML 문서 등의 요소의 위치를 경로로 표현하는 것
스크래핑을 방지할 목적으로 랜덤하게 class 이름을 생성하는 사이트들이 있는데 이 경우 요소의 변하지 않는 위치를 이용한 XPATH로 스크래핑을 진행 할 수 있다.
다음 사이트에 있는 행사의 이름을 스크래핑 해보려고 한다.
이 사이트는 앞서 설명한 class 이름을 생성하는 사이트 중 하나이다.
https://indistreet.com/live?sortOption=startDate%3AASC
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를 이용해서 스크래핑해보자.
.implicitly_wait()을 활용해서 암시적 기다림을 적용할 수 있다.
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)
WebDriverWait()과 두 메서드를 활용해서 명시적 기다림을 적용할 수 있다.
until() : 인자의 조건이 만족될 때까지until_not() : 인자의 조건이 만족되지 않을 때까지EC : expected_conditions로, selenium에서 정의된 조건from selenium.webdriver.support.ui import WebDriverWait
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)
웹 페이지에서 일어나는 일들을 Event라고 한다.
마우스 이벤트
https://www.selenium.dev/documentation/webdriver/actions_api/mouse/
키보드 이벤트
https://www.selenium.dev/documentation/webdriver/actions_api/keyboard/
Selenium으로 이런 이벤트를 처리할 수 있다. 다음 사이트에 로그인해보자.
https://hashcode.co.kr/
마우스 입력은 크게 다음과 같은 과정을 거친다.
find_element() 이용)click을 통해 전달.perform()을 통해 동작키보드 입력은 크게 다음과 같은 과정을 거친다.
find_element() 이용)send_keys_to_element를 통해 전달.perform()을 통해 동작확인해보니 위 사이트에서 네비게이션 바의 로그인 버튼은 UtilMenustyle__Link-sc-2sjysx-4 ewJwEL클래스, 아이디 입력창은 FymRFM681OjzOdzor5nk클래스, 로그인 버튼은 itAWTII94uCyf9uUgREi클래스를 통해 접근 가능함을 확인하였다. 패스워드 입력창은 아이디 입력창과 같은 클래스를 가지기에 XPATH를 알아내여 접근하였다.
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
import time
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get('https://hashcode.co.kr/')
time.sleep(5)
# 내비게이션 바에서 "로그인" 버튼을 찾아 누름
button = driver.find_element(By.CLASS_NAME, 'UtilMenustyle__Link-sc-2sjysx-4.ewJwEL')
ActionChains(driver).click(button).perform()
time.sleep(5)
# 아이디를 입력
id_input = driver.find_element(By.CLASS_NAME, "FymRFM681OjzOdzor5nk")
ActionChains(driver).send_keys_to_element(id_input, "id").perform()
time.sleep(5)
# 비밀번호를 입력
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(5)
# 로그인 버튼을 눌러서 로그인을 완료
login = driver.find_element(By.CLASS_NAME, 'itAWTII94uCyf9uUgREi')
ActionChains(driver).click(login).perform()
time.sleep(5)

정상적으로 작동함을 확인 할 수 있다. 임의로 아이디를 id, 패스워드를 pw로 했으니 당연하게 실패한다 올바른 아이디, 패스워드를 입력하면 로그인이 될 것이다.