본문으로 바로가기

WebDriver, WebElements, Waiting

category Crawler/Crawler 2020. 9. 7. 14:17

WebDriver

 

어떤 웹 드라이버를 사용하는 지에 따라 지정할 수 있는 옵션이나 사용할 수 있는 메서드 등에 조금씩 차이가 있는 편입니다만, 대체로 비슷합니다.

 

대표적인 크롬 웹 드라이버를 가져와봤습니다.

selenium-python.readthedocs.io/api.html#module-selenium.webdriver.chrome.webdriver

 

maximize_window(), get_window_size()와 같은 메서드들이 있으니 참고해보시면 좋겠습니다.

 

 

여기서 눈여겨 볼 메서드는 execute_script입니다. 웹을 띄운 다음 javascript를 실행할 수 있도록 만든 것입니다. 이를 이용해 특정 DOM을 지운다던 지 등의 javasciprt적인 동작을 구현할 수 있게 됩니다. 또, js가 반환하는 값을 python으로 가져올 수도 있습니다.

 

(method) execute_script: (script, *args) -> Any

Synchronously Executes JavaScript in the current window/frame.

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

browser = webdriver.Chrome(ChromeDriverManager().install())

browser.execute_script("""
    const payload = arguments[0]
    alert(payload)
""", "hello")

 

반대로 javascript를 브라우저에서 실행한 결과를 python 코드로 반환할 수도 있습니다. return하면 됩니다.

 

browser.set_window_size(size, 1936)

    # return 한 값이 python 변수에 담길 수 있습니다.
    scroll_size = browser.execute_script("""
        return document.body.scrollHeight;
    """)
    print(scroll_size)
    time.sleep(3)

 

 

Location Elements & WebElements

 

공식 문서에 가면 Locating Elements에 대한 짤막한 글이 있습니다. 평이하니 훑어봅시다.

(selenium-python.readthedocs.io/locating-elements.html)

 

주의할 점은 여러 요소를 가져올 것인지, 발견되는 첫번째 요소만 가져올 것인지에 대해 's' (복수형) 하나로 구별되기 때문에 실수하기 쉽다는 점입니다. 

  • find_element_by_class_name
  • find_elements_by_class_name

여기서 살펴볼 내용은 Elements를 찾는 메서드가 반환하는 WebElement 객체에서 사용할 수 있는 속성과 메서드들이 꽤나 유용하니 알아둡시다.

 

예를 들어 단순히 click()을 하거나 screenshot을 찍을 수도 있으며 반환 받은 WebElement 내부에서 또 다시 find_elements_by_class_name 등으로 웹요소들을 찾을 수도 있고, send_keys를 통해 원하는 값을 입력할 수도 있습니다.

 

일일히 다 설명하는 것보다 관련 문서를 보는 것이 더 효율적이므로 링크를 첨부합니다.

 

selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webelement.WebElement

 

7. WebDriver API — Selenium Python Bindings 2 documentation

A single IP address, as a string. If any IPv4 address is found, one is returned. Otherwise, if any IPv6 address is found, one is returned. If neither, then None is returned.

selenium-python.readthedocs.io

 

 

Waiting

js로 로드되는 페이지들. 즉, javascript 기반 SPA나 ajax를 사용하는 웹사이트 등은 검색 즉시 내용물이 나오는 것이 아니라 자바스크립트가 내용물을 로드할 때까지 기다려줘야 한다. 

 

기다리는 부분은 현대 웹을 크롤링하는데 중요한 부분이라서 selenium도 따로 페이지를 할당해 설명하고 있다.

selenium-python.readthedocs.io/waits.html

 

공식 문서에서 든 예시는 다음과 같다. 

 

기다리기 위한 WebDriverWait(driver, timeout) 객체는 until 메서드를 가지고 있으며 

인자로 EC, 즉, 기대 조건을 받습니다. EC의 종류는 공식문서를 참고하도록 합시다. verbose하게 풀어줘서 메서드 이름을 읽는 것만으로도 무엇을 의도하는 지 알 수는 있습니다.

 

  • title_is
  • title_contains
  • presence_of_element_located
  • ... 등등

그리고 EC 메서드는 어떤 특징을 가진 요소를 대상으로 기대할 것인지를 인자로 받습니다.

표기법이 조금 독특한데, (()) 괄호를 두 번 쳐야 합니다.

 

(By.ID 는 요소의 id가 무엇인지를 말하고, 그 다음 인자는 어떤 id를 기다릴 것인지를 의미합니다. 상식적입니다.)

 

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

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()

 

예시를 들자면 다음과 같습니다.

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By

from webdriver_manager.chrome import ChromeDriverManager

browser = webdriver.Chrome(ChromeDriverManager().install())

KEYWORD = "buy domain"

# google로 접속
browser.get("https://google.com")

# 검색창 찾은 후 입력, 엔터
search_bar = browser.find_element_by_class_name("gLFyf")
search_bar.send_keys(KEYWORD)
search_bar.send_keys(Keys.ENTER)


# 곧장 찾으려하자 에러! 아직 로드가 안되었기 때문
noElement = browser.find_element_by_class_name("g kno-kp mnr-c g-blk")

# 기다려줘야 함
needToRemoveElement = WebDriverWait(browser, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, "g-blk")))

print(needToRemoveElement) // WebElement로 포착한 것을 볼 수 있다.


 

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체