Programing Language/Python

Selenium 옵션과 이벤트 처리 코드 모음

jheaon 2023. 7. 4. 07:24

 

 

해당 게시글은 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 옵션과 이벤트 처리 코드 모음

관련글