파이썬(Python)/웹스크롤링

파이썬 Selenium 페이지 로딩 대기하는 방법

끄적끄적아무거나 2021. 1. 24. 20:25
반응형

최근 웹 어플리케이션은 AJAX를 많이 사용한다. 즉, 페이지 전체를 로딩하는 것이 아닌 일부만 서버와 AJAX 통신을 하는 것이다. 그래서 브라우져에서 페이지를 로딩시 페이지에 element 들이 다른 시간대에 도착할 수 있다.

만일 페이지는 불렀고 우너하는 element가 아직 로딩 안된 상태에서 해당 위치를 selenium으로 찾으려고 하면 실패한다. ElementNotVisibleException 를 리턴해주게 된다. 

셀레늄에는 두가지 방법으로 원하는 element가 로딩되는 시간을 기다려 준다. 

 

- Explicit Waits

- Implicit Waits

 

1. Explicit Waits

 

Explicit Waits 는 파이썬 개발자가 정한 조건이 될때까지 기다렸다가 되면 다음으로 진행한다. 조건문은 util 뒤의 괄호안에 코드이다. 비슷하게 동작하는 방식이 time.sleep()이다. time.sleep()은 괄호안의 시간이 될 동안 대기하고 다음 코드를 실행한다. 즉, 페이지가 로딩될 충분한 시간을 주면 문제 없이 코드를 진행할 수 있는 것이다. 하지만 그런 방식은 불필요한 시간까지 소비하게 된다. 

 

일반적으로 사용하는 방식은 내가 찾는 element가 있으면 element가 로딩될때까지 기다리는 조건을 걸고 로딩이 되면 그정보를 이용해서 다음 작업을 진행하는 것이다. 

 

예제>>

        try:
            username_box_check = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located\
                ((By.XPATH, '//*[@id="loginForm"]/div/div[1]/div/label/input')))
            print("username_box_check : ", username_box_check)
        except:
            print("error")

 

결과>>

username_box_check :  <selenium.webdriver.remote.webelement.WebElement (session="9773c09ba5c8aac402a11d805c55032c", element="c341c973-a528-4945-9071-fe2e3a04ae85")>

 

주석>>

위 예제는 인스타그램에서 로그인할때 ID input 창이 로딩되었는지 확인하는 예제이다.

 

위 코드에서 until 뒤에 xpath에서 '//*[@id="loginForm"]/div/div[1]/div/label/input' 가 로딩 될때까지 기다리는데 최대 10초까지 기다린다. 10초 내에 해당 값이 로딩이 되지 않으면 except 문으로 빠져 나간다. 10초내에 해당 값이 로딩이 되면 로딩이 되자마자 (10초 대기하지 않음) try문을 완료 하고 다음 코드를 진행하게 된다. 

 

결과에서 볼 수 있듯이 print문이 제대로 나왔음을 알 수 있다.

 

위 예제의 조건은 "presence_of_element_located"이다. 함수명에서 알 수 있듯이 element가 존재하는지 여부이다. 해당 함수 명 이외에도 여러가지 조건을 걸 수 있다. 아래는 "presence_of_element_located" 말고 다른 조건문이다. 함수명을 통해 어떤 기능인지는 쉽게 알 수 있다. 잘 이해가 안된다면 검색을 통해 찾아보기를 권한다.

 

- title_is
- title_contains
- presence_of_element_located
- visibility_of_element_located
- visibility_of
- presence_of_all_elements_located
- text_to_be_present_in_element
- text_to_be_present_in_element_value
- frame_to_be_available_and_switch_to_it
- invisibility_of_element_located
- element_to_be_clickable
- staleness_of
- element_to_be_selected
- element_located_to_be_selected
- element_selection_state_to_be
- element_located_selection_state_to_be
- alert_is_present

 

위의 조건이외에 개발자가 직접 조건문을 class 함수로 만들 수 있다. 

아래 예제 코드는 css class 명을 통해 대기하는 방식인데 __call__ method를 사용해서 만들었다.

class element_has_css_class(object):
  """An expectation for checking that an element has a particular css class.

  locator - used to find the element
  returns the WebElement once it has the particular css class
  """
  def __init__(self, locator, css_class):
    self.locator = locator
    self.css_class = css_class

  def __call__(self, driver):
    element = driver.find_element(*self.locator)   # Finding the referenced element
    if self.css_class in element.get_attribute("class"):
        return element
    else:
        return False

# Wait until an element with id='myNewInput' has class 'myCSSClass'
wait = WebDriverWait(driver, 10)
element = wait.until(element_has_css_class((By.ID, 'myNewInput'), "myCSSClass"))

 

2. Implicit Waits

 

Implicit Waits 은 해당 element가 없으면 폴링방식으로 다른 프로세스를 진행하고 어느 시간이 지난 후 다시 해당 element가 있는지 찾는 방식이다. 여기서 다른 프로세스는 웹 어플리케이션을 얘기 하고 있으므로 DOM이다. 

 

예제>>

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

options = webdriver.ChromeOptions() 
# options.headless = True
options.add_experimental_option("excludeSwitches", ["enable-logging"])
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64)/AppleWebKit/537.36 \
    (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36")
driver = webdriver.Chrome(options=options)
# driver.implicitly_wait(100) # seconds
driver.get("https://www.instagram.com/")
ele = driver.find_element_by_xpath('//*[@id="loginForm"]/div/div[2]/div/label/input')
print(ele) 

 

결과>>

<selenium.webdriver.remote.webelement.WebElement (session="f7e35237a40cf4e52386177b55c9bde4", element="b300c16e-0dc3-469b-8c40-a387a49575bb")>

 

주석>>

위 예제는 인스타 그램의 로그인에서 패스워드 부분 element를 가져오는 예제이다. 우선 처음에 코드를 위처럼 driver.implicitly_wait(100) 부분을 주석 처리해서 Implicit Waits 을 사용하지 않았다. 안타깝게도(?) 요즘 서버는 빨라서 wait 하지않아도 결과가 나와서 VPN을 사용해서 외국에서 거쳐서 접근을 하니 위 인스타 캡쳐 화면 처럼 로그인 부분이 페이지 로딩에 비해 느리게 진행되었다. 물론 코드 결과도 에러가 발생하였다.

 

그래서 driver.implicitly_wait(100) 을 추가해서 코딩을 하니 Implicit wait 방식이 적용되서 페이지가 로딩뒤에 다시 돌아와 결과 값을 출력해주었다.

 

 

 

반응형