정적 웹사이트 vs 동적 웹사이트
정적 웹사이트는 움직임이 없는 고정된 웹사이트이고, 동적 웹사이트는 움직임이 많은 다이나믹한 웹사이트이다. 여기서 움직임이란 HTML node의 움직임으로, 웹사이트가 처음 로딩된 후에 사용자가 어떤 행동을 하여 웹사이트에 변화가 있는 경우 움직임이 있다고 표현한다.
정적 웹사이트는 브라우저에 렌더링 할 때 처음에 HTML이 주는 그대로 화면에 그려내면 된다. 즉, HTML과 CSS를 그리는데, 처음에 웹 서버와 통신할 때 받아와서 렌더링하고 그 후에는 웹 서버와 통신을 하지 않는다. 동적 웹사이트는 JS 를 그려낸다고 보면 된다. 사용자가 특정 행동을 하면 웹서버에서 새로운 데이터를 새로 가져와서 다시 렌더링을 하는 것이다. 따라서 웹 서버와의 통신이 잦다.
셀레니움
셀레니움은 브라우저 테스팅 툴 또는 브라우저 원격 조종 툴로 동적 크롤링에 많이 사용된다. 페이스북에서 무한 스크롤 기능이 잘 작동하는지 테스트 해야할 떄 사람이 계속 스크롤을 내리면서 작동이 잘 되는지 테스트를 하는것이 아니라 컴퓨터한테 자동화 테스트를 시켜서 인력을 최소화한다. 이 자동화 테스트를 가능하게 하는 툴이 셀레니움이다. 셀레니움은 브라우저와 TCP 통신을 한다.
selenium 패키지를 다운 받는다.
pip install selenium
다음으로 자신의 크롬 버전을 확인하여 그에 맞는 크롬 드라이버를 https://chromedriver.chromium.org/downloads에서 다운 받은 후에 실행시킬 파이썬 파일과 같은 경로에 넣어준다.
from selenium import webdriver
import time
# 셀레니움으로 크롬 조작하기 (naver.com 10초동안 열었다가 닫기)
# 셀레니움은 크롬을 디버그 모드로 켜서 조작함
browser = webdriver.Chrome('./chromedriver.exe')
browser.get('http://naver.com')
time.sleep(10)
browser.close()
도커로 셀레니움을 실행하려면 아래와 같이 하면 된다.
$ docker run -p 4444:4444 selenium/standalone-chrome
도커로 실행하면 크롬 드라이버가 따로 없어도 실행가능하다.
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
browser = webdriver.Remote("http://127.0.0.1:4444/wd/hub", DesiredCapabilities.CHROME) # 셀레니움에게 크롬을 사용할 것이라고 알려줌
browser.get("http://naver.com")
print(browser.title)
browser.close()
페이지 이동
get으로 페이지를 이동할 수 있다.
from selenium import webdriver
import time
'''
셀레니움이 디버그 모드로 크롬을 켤 때 우리가 사용하던 extension들을 같이 켜주지 않음
-> options.add_encoded_extension()으로 추가해줘야 함
'''
options = webdriver.ChromeOptions()
options.add_argument('window-size=1000,1000') # 1000x1000 해상도로 크롬 창을 실행
options.add_argument('no-sandbox') # 크롬의 각 탭은 별개의 프로그램(보안 격리를 위함)인데 no-sandbox 옵션을 주면 자유롭게 탭을 이동하면서 크롤링이 가능함
# options.add_argument('headless') # 크롬창을 보이지 않게함 (CPU 사용량 최소화)
chrome = webdriver.Chrome('./chromedriver.exe', options=options)
chrome.get('https://naver.com') # 해당 페이지로 이동 (requests 의 get과 다름)
chrome.get('https://shopping.naver.com')
chrome.back() # 전 페이지로 이동
chrome.forward() # 다음 페이지로 이동
time.sleep(3)
chrome.close()
페이지 로딩
만약에 페이지가 로딩이 아직 완료되지 않은 상태에서 검색을 실행하면 에러가 난다. 셀레니움은 기본적으로 페이지 로딩을 기다려주는 JS 가 실행되는 그 쯤까지만 기다려준다. 아래 그림은 웹 페이지가 로딩되는 순서를 그림으로 그린 것인데, 셀레니움은 HTML과 CSS와 같이 로드되는 JS까지는 기다려주지만, HTML과 CSS 로드된 후에 로드되는 JS는 기다려주지 않는다. HTML과 CSS 로드된 후에 로드되는 JS를 onload라고 하는데 onload들이 엮여 있는것을 onload chaining이라고 한다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
options = webdriver.ChromeOptions()
options.add_argument('window-size=1000,1000')
options.add_argument('no-sandbox')
chrome = webdriver.Chrome('./chromedriver.exe', options=options)
chrome.get('https://shopping.naver.com')
# 1. time.sleep(): 파이썬 프로그램을 멈춤
time.sleep(10)
# 2. implicitly_wait(): 크롬 드라이버를 멈춤
chrome.implicitly_wait(10)
# 3. 우리가 원하는 element가 표시될 때까지 멈춤
# input[name=query] css selector가 나타날때까지 최대 10초 기다리기
WebDriverWait(chrome, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[name=query]")))
chrome.close()
element 찾기
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
options = webdriver.ChromeOptions()
options.add_argument('window-size=1000,1000')
options.add_argument('no-sandbox')
chrome = webdriver.Chrome('./chromedriver.exe', options=options)
chrome.get('https://shopping.naver.com')
# css selector 기반으로 element 찾기
wait = WebDriverWait(chrome, 10)
el = WebDriverWait(chrome, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[name=query]")))
print(el)
chrome.close()
element 클릭
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
options = webdriver.ChromeOptions()
options.add_argument('window-size=1000,1000')
options.add_argument('no-sandbox')
chrome = webdriver.Chrome('./chromedriver.exe', options=options)
chrome.get('https://shopping.naver.com')
wait = WebDriverWait(chrome, 10)
def find(wait, css_selector):
return wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, css_selector)))
# 검색어 입력
search = find(wait, "input[name=query]")
search.send_keys('아이폰')
time.sleep(3)
# 검색 버튼 클릭
button = find (wait, 'a.co_srh_btn')
button.click()
time.sleep(3)
# 검색어 입력 및 검색 실행
search.send_keys('아이폰\n')
chrome.close()