해당 게시글은 https://jheaon.tistory.com/127 에서 동적 크롤링 부분에서 설명한 "파이썬을 활용한 업무 자동화" 위키 독스의 내용을 정리한 것이다.
파이썬과 request, bs4, selenium4을 통해 웹 페이지 데이터 크롤링하기
일반적으로 인터넷을 이용하면서 웹에 있는 데이터를 가공하거나 사용해야 하는 경우가 빈번하다. 특히 사이트에서 어떤 가격에 대한 데이터를 받아 엑셀로 저장하여 정리하여야 한다는 가, 여
jheaon.tistory.com
01. selenium 4
[TOC] ## 셀레니움 설치 ```{.python} pip install selenium # pip install selenium-wire # 특정 버전 설치 pip insta…
www.wikidocs.net
크롬 드라이버 설치, 세션 시작
🖥️ main.py
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
options = webdriver.ChromeOptions()
# 크롬을 백그라운드로 실행시키고 싶다면 해당 옵션 추가
# options.add_argument("headless")
# 모바일로 사이트에 접속하고 싶다면 해당 옵션 추가
# mobile_emulation = { "deviceName": "iPhone 12 Pro" }
# options.add_experimental_option("mobileEmulation", mobile_emulation)
# options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
options.add_experimental_option('excludeSwitches', ['enable-logging'])
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_experimental_option("detach", True)
service = Service(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
driver.get(url)
페이지 접속, 상호작용
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
driver.back()
driver.forward()
driver.refresh()
페이지 정보 가져오기
title = driver.title
url = driver.current_url
handle = driver.current_window_handle
request headers 가져오기
# pip install selenium-wire
# from selenium import webdriver 부분만 아래와 같이 대체
from seleniumwire import webdriver
driver.get("https://pypi.org/project/selenium-wire/")
for request in driver.requests:
print(request.url) # <——————— Request url
print(request.headers) # <————— Request headers
print(request.response.headers) # <————— Response headers
대기시간 처리
# 암시적 대기
driver.implicitly_wait(0.5)
# 명시적 대기 (필요한 요소가 로드될 때 까지 대기)
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def document_initialised(driver):
return driver.execute_script("return initialised")
WebDriverWait(driver, timeout=10).until(document_initialised)
WebDriverWait(driver, timeout=10).until(EC.presence_of_element_located((By.XPATH, xpath_page)))
WebDriverWait(driver, timeout=3).until(lambda d: d.find_element(By.TAG_NAME,"p"))
# Fluent 대기 (조건 기다리는 최대 시간과 조건 확인 빈도 정의, 대기 동안 특정 유형 예외 무시 가능)
wait = WebDriverWait(driver, timeout=10, poll_frequency=1, ignored_exceptions=[ElementNotVisibleException, ElementNotSelectableException])
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//div")))
요소 찾기 (element finder)
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
# 첫 번째 일치 요소 찾기
vegetable = driver.find_element(By.CLASS_NAME, "tomatoes")
from selenium.webdriver.common.by import By
driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_element(By.XPATH, '//button')
driver.find_element(By.ID, 'loginForm')
driver.find_element(By.LINK_TEXT, 'Continue')
driver.find_element(By.PARTIAL_LINK_TEXT, 'Conti')
driver.find_element(By.NAME, 'username')
driver.find_element(By.TAG_NAME, 'h1')
driver.find_element(By.CLASS_NAME, 'content')
driver.find_element(By.CSS_SELECTOR, 'p.content')
# DOM의 하위집합 평가
fruits = driver.find_element(By.ID, "fruits")
fruit = fruits.find_element(By.CLASS_NAME,"tomatoes")
# 최적화 요소
fruit = driver.find_element(By.CSS_SELECTOR,"#fruits .tomatoes")
# 일치하는 모든 요소
plants = driver.find_elements(By.TAG_NAME, "li")
# 모든 요소 리스트에서 각 요소 가져오기
elements = driver.find_elements(By.TAG_NAME, 'p')
for e in elements:
print(e.text)
# 요소에서 요소찾기
element = driver.find_element(By.TAG_NAME, 'div')
elements = element.find_elements(By.TAG_NAME, 'p')
for e in elements:
print(e.text)
# 활성 요소 (현재 포커스가 있는)
attr = driver.switch_to.active_element.get_attribute("title")
CLASS_NAME | 클래스 이름에 검색 값이 포함된 요소를 찾습니다(복합 클래스 이름은 허용되지 않음). |
CSS_SELECTOR | CSS 선택기와 일치하는 요소를 찾습니다. |
ID | ID 속성이 검색 값과 일치하는 요소를 찾습니다. |
NAME | NAME 속성이 검색 값과 일치하는 요소를 찾습니다. |
LINK_TEXT | 보이는 텍스트가 검색 값과 일치하는 앵커 요소를 찾습니다. |
PARTIAL_LINK_TEXT | 보이는 텍스트에 검색 값이 포함된 앵커 요소를 찾습니다. 여러 요소가 일치하는 경우 첫 번째 요소만 선택됩니다. |
TAG_NAME | 태그 이름이 검색 값과 일치하는 요소를 찾습니다. |
XPATH | XPath 표현식과 일치하는 요소를 찾습니다. |
요소(element)와 상호작용 하기
click | 모든 요소에 적용 | element.click() |
send_keys | 텍스트 필드 및 콘텐츠 편집 가능한 요소에만 적용됨. | element.send_keys("webdriver" + Keys.ENTER) |
clear | 텍스트 필드 및 콘텐츠 편집 가능한 요소에만 적용됨 | element.clear() |
submit | Form 요소에만 적용됨 | element.submit() |
select | 모든 요소에 적용 | -------- |
요소(element) 정보 가져오기
크기 및 위치 | 요소의 치수와 좌표 | driver.find_element(By.CSS_SELECTOR, "h1").rect |
CSS 값 | 요소의 스타일 속성 | driver.findElement(By.LINK_TEXT, "More information...").value_of_css_property('color') |
텍스트 내용 | 요소의 렌더링된 텍스트 | driver.find_element(By.CSS_SELECTOR, "h1").text |
스크롤 작업
# 원하는 요소 위치까지 스크롤
원하는_엘리먼트_위치 = '//*[@id="root"]/div/section/div/div[8]'
element = WebDriverWait(driver, 1).until(EC.presence_of_element_located((By.XPATH, 원하는_엘리먼트_위치)))
loc = element.location['y']
driver.execute_script(f"window.scrollTo(0, {loc})")
time.sleep(0.2)
# 또다른 방법
element = driver.find_element_by_class_name("scrollContentWrap")
driver.execute_script("arguments[0].scrollBy(0, arguments[0].scrollHeight)", element)
# 페이지 최하단까지 스크롤
SCROLL_PAUSE_TIME = 3
last_height = driver.execute_script("return document.body.scrollHeight") # Get scroll height
while True:
# Scroll down to bottom
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# Wait to load page
time.sleep(SCROLL_PAUSE_TIME)
# Calculate new scroll height and compare with last scroll height
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
iFrame 작업
# 아이프레임을 대상으로 드라이버 전환하여 상호작용
iframe = driver.find_element(By.CSS_SELECTOR, "#modal > iframe")
driver.switch_to.frame(iframe)
driver.find_element(By.TAG_NAME, 'button').click()
# 이름 또는 ID를 사용하여 전환
driver.switch_to.frame('buttonframe')
# 인덱스를 사용하여 전환
iframe = driver.find_elements(By.TAG_NAME,'iframe')[1]
driver.switch_to.frame(iframe)
# iFrame 나가 기본 콘텐츠로 돌아가기
driver.switch_to.default_content()
Alert, Confirm, Prompt 팝업 대응
# Alert
driver.find_element(By.LINK_TEXT, "See an example alert").click()
alert = wait.until(expected_conditions.alert_is_present())
text = alert.text
alert.accept()
# Confirm
driver.find_element(By.LINK_TEXT, "See a sample confirm").click()
wait.until(expected_conditions.alert_is_present())
alert = driver.switch_to.alert
text = alert.text
alert.dismiss()
# Prompt
driver.find_element(By.LINK_TEXT, "See a sample prompt").click()
wait.until(expected_conditions.alert_is_present())
alert = Alert(driver)
alert.send_keys("Selenium")
alert.accept()
팝업창 작업
# 팝업창으로 이동/복귀
driver.switch_to.window(driver.window_handles[1])
driver.close() # 각각 종료
driver.switch_to.window(driver.window_handles[0])
driver.close() # 각각 종료
브라우저 창 및 탭 작업
# WebDriver는 창과 탭을 구분하지 않음.
# Selenium에서 창 핸들의 고유 식별자를 사용하여 구분하여 사용 가능.
driver.current_window_handle
# 새 창 또는 탭 만들기 및 전환
driver.switch_to.new_window('tab') # 새 탭 열어 포커스 전환
driver.switch_to.new_window('window') # 새 창 열어 포커스 전환
# 현재 창 또는 탭 닫기 (닫은 후에는 반드시 이전 창으로 전환해 작업을 이어가야 함)
driver.close()
driver.switch_to.window(original_window)
창 관리
# 창 크기 가져오기 (가로 세로 따로)
width = driver.get_window_size().get("width")
height = driver.get_window_size().get("height")
# 창 크기 가져오기 (가로 세로 한 번에 가져와 나중에 각각 조회)
size = driver.get_window_size()
width1 = size.get("width")
height1 = size.get("height")
# 창 크기 설정
driver.set_window_size(1024, 768)
# 창 위치 가져오기 (가로 세로 따로)
x = driver.get_window_position().get('x')
y = driver.get_window_position().get('y')
# 창 위치 가져오기 (가로 세로 한 번에 가져와 나중에 각각 조회)
position = driver.get_window_position()
x1 = position.get('x')
y1 = position.get('y')
# 창 위치 설정
driver.set_window_position(0, 0)
# 창 최대화
driver.maximize_window()
# 창 최소화
driver.minimize_window()
# 전체 화면 창 (F11 누른것 처럼)
driver.fullscreen_window()
스크린 샷
# 스크린 샷
driver.save_screenshot('./image.png')
# element 스크린 샷
ele = driver.find_element(By.CSS_SELECTOR, 'h1')
ele.save_screenshot('./image.png')
스크립트 실행
# 선택한 프레임 또는 창의 현재 컨텍스트에서 자바스크립트 실행
header = driver.find_element(By.CSS_SELECTOR, "h1")
driver.execute_script('return arguments[0].innerText', header)
페이지 인쇄
# 브라우저 내에서 현재 페이지를 인쇄, 크롬브라우저가 headless 모드여야 함.
from selenium.webdriver.common.print_page_options import PrintOptions
print_options = PrintOptions()
print_options.page_ranges = ['1-2']
driver.get("printPage.html")
base64code = driver.print_page(print_options)
세션 종료
driver.close() # 탭 종료
driver.quit() # 콘솔까지 완전 종료
Shadow DOM 요소 접근
# selenium 4
shadow_host = driver.find_element(By.CSS_SELECTOR, '#shadow_host')
shadow_root = shadow_host.shadow_root
shadow_content = shadow_root.find_element(By.CSS_SELECTOR, '#shadow_content')
사용 중단 경고 방지
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
service = ChromeService(executable_path=CHROMEDRIVER_PATH)
driver = webdriver.Chrome(service=service, options=options)
쿠키 작업
쿠키 추가 | driver.add_cookie({"name": "test1", "value": "value"}) |
이름이 지정된 쿠키 가져오기 | print(driver.get_cookie("test1")) |
모든 쿠키 가져오기 | driver.get_cookies() |
이름이 일치하는 쿠키 삭제 | driver.delete_cookie("test1") |
모든 쿠키 삭제 | driver.delete_all_cookies() |
ActionChains
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver)\
.move_to_element(clickable)\
.pause(1)\
.click_and_hold()\
.pause(1)\
.send_keys("abc")\
.perform()
# 모든 작업 해제 (마지막 위치 등을 계속 기억하고 있음. 해제하면 초기화 됨)
ActionBuilder(driver).clear_actions()
# 키보드 값 참고
https://github.com/SeleniumHQ/selenium/blob/selenium-4.2.0/py/selenium/webdriver/common/keys.py#L23
# key down
ActionChains(driver)\
.key_down(Keys.SHIFT)\
.send_keys("abc")\
.perform()
# key up
ActionChains(driver)\
.key_down(Keys.SHIFT)\
.send_keys("a")\
.key_up(Keys.SHIFT)\
.send_keys("b")\
.perform()
# send keys
ActionChains(driver)\
.send_keys("abc")\
.perform()
# 지정항목
text_input = driver.find_element(By.ID, "textInput")
ActionChains(driver)\
.send_keys_to_element(text_input, "abc")\
.perform()
# 복사 및 붙여넣기
cmd_ctrl = Keys.COMMAND if sys.platform == 'darwin' else Keys.CONTROL
ActionChains(driver)\
.send_keys("Selenium!")\
.send_keys(Keys.ARROW_LEFT)\
.key_down(Keys.SHIFT)\
.send_keys(Keys.ARROW_UP)\
.key_up(Keys.SHIFT)\
.key_down(cmd_ctrl)\
.send_keys("xvv")\
.key_up(cmd_ctrl)\
.perform()
# 마우스 길게 클릭
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver)\
.click_and_hold(clickable)\
.perform()
# 마우스 클릭하고 해제
clickable = driver.find_element(By.ID, "click")
ActionChains(driver)\
.click(clickable)\
.perform()
# 마우스 버튼 정의 값
0 — 왼쪽 버튼(기본값)
1 — 가운데 버튼(현재 지원되지 않음)
2 — 오른쪽 버튼
3 — X1(뒤로) 버튼
4 — X2(앞으로) 버튼
# 컨텍스트 클릭 (오른쪽 클릭, 요소의 중앙으로 이동 후 오른쪽 클릭)
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver)\
.context_click(clickable)\
.perform()
# 마우스 뒤로 클릭
action = ActionBuilder(driver)
action.pointer_action.pointer_down(MouseButton.BACK)
action.pointer_action.pointer_up(MouseButton.BACK)
action.perform()
# 마우스 앞으로 클릭
action = ActionBuilder(driver)
action.pointer_action.pointer_down(MouseButton.FORWARD)
action.pointer_action.pointer_up(MouseButton.FORWARD)
action.perform()
# 마우스 더블 클릭
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver)\
.double_click(clickable)\
.perform()
# 요소로 이동
hoverable = driver.find_element(By.ID, "hover")
ActionChains(driver)\
.move_to_element(hoverable)\
.perform()
# 요소에서 오프셋 이동 (요소 왼쪽 상단 원점 기준)
mouse_tracker = driver.find_element(By.ID, "mouse-tracker")
ActionChains(driver)\
.move_to_element_with_offset(mouse_tracker, 8, 0)\
.perform()
# 요소로부터 간격 띄우기 (요소 중심 원점 기준)
??
# 뷰포트에서 오프셋 (현재 뷰포트의 왼쪽 상단 모서리에서 마우스 이동)
action = ActionBuilder(driver)
action.pointer_action.move_to_location(8, 0)
action.perform()
# 현재 포인터 위치에서 오프셋
ActionChains(driver)\
.move_by_offset( 13, 15)\
.perform()
# 요소에서 끌어서 놓기
draggable = driver.find_element(By.ID, "draggable")
droppable = driver.find_element(By.ID, "droppable")
ActionChains(driver)\
.drag_and_drop(draggable, droppable)\
.perform()
# 오프셋으로 드래그 앤 드롭
draggable = driver.find_element(By.ID, "draggable")
start = draggable.location
finish = driver.find_element(By.ID, "droppable").location
ActionChains(driver)\
.drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y'])\
.perform()
# 요소로 스크롤
iframe = driver.find_element(By.TAG_NAME, "iframe")
ActionChains(driver)\
.scroll_to_element(iframe)\
.perform()
# 주어진 양만큼 스크롤
footer = driver.find_element(By.TAG_NAME, "footer")
delta_y = footer.rect['y']
ActionChains(driver)\
.scroll_by_amount(0, delta_y)\
.perform()
# 주어진 양만큼 요소에서 스크롤
iframe = driver.find_element(By.TAG_NAME, "iframe")
scroll_origin = ScrollOrigin.from_element(iframe)
ActionChains(driver)\
.scroll_from_origin(scroll_origin, 0, 200)\
.perform()
# 오프셋이 있는 요소에서 스크롤
footer = driver.find_element(By.TAG_NAME, "footer")
scroll_origin = ScrollOrigin.from_element(footer, 0, -50)
ActionChains(driver)\
.scroll_from_origin(scroll_origin, 0, 200)\
.perform()
# 주어진 양만큼 원점(요소)의 오프셋에서 스크롤
ActionChains(driver)\
.scroll_from_origin(scroll_origin, 0, 200)\
.perform()
'Programing Language > Python' 카테고리의 다른 글
Selenium과 Pyinstaller를 이용하여 만든 실행파일 뒤에 검은창이 나올 경우 해결 방법 (0) | 2023.07.06 |
---|---|
Pyinstaller을 통해 py 파일을 각 OS에 맞는 실행 파일로 변환 (0) | 2023.07.06 |
텔레그램 API 사용시 RuntimeWarning: coroutine 'Bot.get_updates' was never awaited 오류 해결방법 (3) | 2023.06.29 |
키값이나 환경변수를 관리하기 (0) | 2023.06.29 |
컬렉션 객체의 내장 함수 시간 복잡도 모음 (0) | 2023.06.29 |