웹 페이지는 어떻게 생성되냐에 따라 크게 2가지로 구분
1. HTML 내용이 고정된 정적(static) 웹 사이트
-> ex) 변경이 없는 사이트
-> 같은 주소로 보내면 항상 같은 값을 받음
-> HTML 문서가 완전하게 응답
2. HTML 내용이 변하는 동적(dynamic) 웹사이트
-> ex) 인스타그램
-> 페이지가 온 다음 실행됨
-> 응답 후 HTML이 렌더링이 될 때까지의 지연시간존재
웹 브라우저에선 JavaScript라는 프로그래밍 언어가 동작한다.
비동기 처리를 통해 필요한 데이터를 채운다.
-> 동기처리 : 요청에 따른 응답을 기다린다
-> 동기 처리된 경우, HTML 로딩에 문제가 없다.
-> 비동기처리 : 요청에 따른 응답을 기다리지 않는다
-> 상황에 따라서 데이터가 완전하지 않은 경우가 발생한다.
동적 웹사이트는 응답 후 바로 정보를 추출하기 어렵다.
또한, 다양한 키보드 입력과 마우스 클릭 등의 상호작용이 존재한다.
이런 상황을 해결하기 위해, 웹 브라우저를 파이썬으로 조작하는 전략을 취하자.
selenium
은 Python을 이용해서 웹 브라우저를 조작할 수 잇는 자동화 프레임워크이다.
이를 사용하기 위해서 selenium
프레임워크를 설치해야한다.
pip install
을 통해 설치할 수 있다.
pip install selenium
웹 브라우저와 연동을 위해서는 WebDriver
가 필요하다
WebDriver
는 웹 브라우저를 제어할 수 있는 프레임워크이다.
pip install
을 통해 webdriver를 관리하는 라이브러리 webdriver-manager
를 설치한다.
pip install webdriver-manager
설치가 완료되면 Webdriver를 사용할 수 잇다.
컴퓨터에 Chrome이 깔려있어야 한다.
Selenium을 사용하기 위해 우선 Selenium을 불러와야한다.
# selenium으로 부터 webdriver 모듈 불러오기
from selenium import webdriver
from selenium.webdriver.chrome.service import service
from webdriver_manager.chrome import ChromeDriverManager
불러온 모듈 webdriver에서 Chrome()객체를 생성한다.
driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()))
크롬이 실행된다. 명령어를 통해 파이썬과 연동한 것이다.
.get(url)
을 활용하여 요청을 보낸다.
#http://www.example.com으로 요청 보내기
driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()))
driver.get('http://www.example.com')
응답을 받은 후, page_source
속성을 통해 Response의 HTML 문서를 확인할 수 있다.
#page_source 속성 확인
driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()))
driver.get('http://www.example.com')
print(driver.page_source)
프로그램들을 실행하다보면 Chrome 창이 계속 켜져 있는다. 이는 해당 창을 끄라고 명령을 한 적이 없기 때문이다.
with-as
구문을 통해 명령이 끝나면 driver를 종료하도록 설정할 수 있다.
#with-as를 사용하기
with webdriver.Chrome(service = Service(ChromeDriverManager().install())) as driver:
driver.get('http://www.example.com')
print(driver.page_source)
selenium은 받아온 응답으로붜 특정 요소를 추출할 수 있다.
응답을 가지고 있는 dirver/ 요소에 대해 다음 메서드를 적용할 수 있다.
요소 하나 찾기
.find_element(by, target)
by
: 대상 찾는 기준 ID
, TAG_NAME
, CLASS_NAME
,...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(service = Service(ChromeDriverManager().install())) as driver:
driver.get('http://www.example.com')
for element in driver.find_elements(By.TAG_NAME,"p"):
print("Text: ", element.text)
selenium은 동적 웹 사이트에 대한 지원을 진행하기 위해 명시적 기다림(Explicit Wait) 과 암묵적 기다림(Implicit Wait) 를 지원한다.
다음 사이트에 있는 행사의 이름들을 스크래핑 하기 : 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
스크래핑 방지 목적으로 랜덤하게 class 이름을 생성한다.
이러한 경우 쓸 수 있는 방법이 여러가지 있는데,
그 중 한 가지 방법이, 위치를 활용한 방법이다.
XPath
는 XMAL, HTML 문서 요소의 위치를 경로로 표현하는 것을 의미한다.
데스크탑/폴더1/폴더2/음악.mp3
와 비슷하다.
이를 적용하여 데이터를 가져오자.
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div/a/div[2]/p[1]
이다.# 예시 사이트에 요청을 진행하고, 예시 사이트의 첫 번째 이벤트 제목 가져오기
# 스크래핑 원하는 위치에서 우클릭, copy, copy Xpath
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
를 사용해야한다.
2가지 Wait 방법을 활용해보려한다.
implicitly_wait()
를 활용해 암식적 기다림을 활용할 수 있다.
# 10초 동안 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) #요청이 완벽하게 오거나 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()
: 인자의 조건이 만족되지 않을 때까지
예를 들어, id가 target
인 요소가 존재할 때까지 기다린 후 다음 명령을 진행한다.
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,"target")))
이때, EC 는 expenced_conditions
로 , selenium에서 정의된 조건이다.(~가 존재하면)
주어진 XPath가 등장할 때까지 Wait 진행하기 EC.presence_of_element_located()
활용
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")
#explicit wait 변경
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)
여러 공연 제목 가져오기
여러 공연의 제목들을 가져오기 위해 XPath 관찰하기
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[2]/div/a/div[2]/p[1]
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[3]/div/a/div[2]/p[1]
# 10개의 이름을 스크래핑하는 코드 작성
#Explicit Wait
with webdriver.Chrome(service = Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
for i in range(1,11):
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((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)
#Implicit Wait
with webdriver.Chrome(service = Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(10)
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)
로그인 과정을 자동화 하는 것을 목표
다음 사이트에 로그인 하기 위해 "로그인" 버튼 눌러보기 : https://hashcode.co.kr/
웹 페이지에서 일어나는 일을 Event라고 한다.
마우스로 일어날 수 있는 대표적인 이벤트는 다음과 같다.
마우스 움직이기(move)
마우스 누르기(press down)
마우스 떼기(press up)
...
마우스 입력은 다음과 같은 과정을 거친다.
입력하고자 하는 대상 요소를 찾는다.(find_element()
이용)
입력하고자 하는 내용을 click
을 통해 전달한다.
.perform()
을 통해 동작한다.
id
가 button인 요소를 클릭하는 예시이다.
button = driver.find_element(By.ID,"button")
ActionChains(driver).click(button).perform()
# 스크래핑에 필요한 라이브러리 불러오기
from selenium import webdriver
from selenium.webdriver import ActionChains
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://hashcode.co.kr/")
driver.implicitly_wait(0.5)
button = driver.find_element(By.CLASS_NAME, "nav-link.nav-signin")
ActionChains(driver).click(button).perform()
웹 페이지에서 일어나는 일을 Event라고 한다.
키보드로 일어날 수 있는 대표적인 이벤트는 다음과 같다.
키보드 누르기(press down)
키보드 떼기(press up)
...
입력창(input
태그의 form)에 키보드 입력을 진행하는 것을 목표로 한다.
키보드 입력은 다음과 같은 과정을 거친다.
입력하고자 하는 대상 요소를 찾는다.(find_element()
이용)
입력하고자 하는 내용을 send_keys_to_element
를 통해 전달한다.
.perform()
을 통해 동작한다.
id
가 textInput인 요소에 "abc"를 입력하는 예시이다.
text_input = driver.find_element(By.ID, "textInput")
ActionChains(driver).send_keys_to_element(text_input, "abc").perform()
코드 짜보기
# 스크래핑에 필요한 라이브러리 불러오기
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time
driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()))
driver.get("https://www.naver.com")
time.sleep(0.5)
#내비게이션 바에서 "로그인" 버튼 찾아 누르기
button = driver.find_element(By.CLASS_NAME, "link_login")
ActionChains(driver).click(button).perform()
time.sleep(0.5)
#"아이디" input 요소에 아이디 입력
id_input = driver.find_element(By.ID,"id")
ActionChains(driver).send_keys_to_element(id_input,"hello").perform()
time.sleep(1)
#"패스워드" input 요소에 비밀번호 입력
pw_input = driver.find_element(By.ID,"pw")
ActionChains(driver).send_keys_to_element(id_input,"hello").perform()
time.sleep(1)
#"로그인" 버튼 눌러서 로그인 완료
login_button = driver.find_element(By.ID, "log.login")
ActionChains(driver).click(login_button).perform()
time.sleep(0.5)