Python 용 Selenium WebDriver로 페이지가로드 될 때까지 기다리십시오.


181

무한 스크롤로 구현 된 페이지의 모든 데이터를 긁어 내고 싶습니다. 다음 파이썬 코드가 작동합니다.

for i in range(100):
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(5)

이것은 맨 아래로 스크롤 할 때마다 5 초 동안 기다려야한다는 것을 의미합니다. 일반적으로 페이지가 새로 생성 된 내용의로드를 마치기에 충분합니다. 그러나 이것은 시간 효율적이지 않을 수 있습니다. 페이지가 5 초 내에 새 내용로드를 완료 할 수 있습니다. 아래로 스크롤 할 때마다 페이지에 새 내용로드가 완료되었는지 어떻게 알 수 있습니까? 이를 감지 할 수 있으면 페이지로드가 완료된 후 다시 아래로 스크롤하여 더 많은 내용을 볼 수 있습니다. 이것은 더 시간 효율적입니다.


1
페이지에 대해 조금 더 알아두면 도움이 될 수 있습니다. 요소가 순차적이거나 예측 가능한가? id 또는 xpath를 사용하여 가시성을 검사하여 요소가로드 될 때까지 기다릴 수 있습니다.
user2272115

다음 페이지를 크롤링합니다. pinterest.com/cremedelacrumb/yum
apogne

1
가능한 복제 페이지
kenorb

이것이 귀하의 질문에 대답합니까? Selenium에서 페이지로드 대기
Matej J

답변:


234

webdriver통해 기본적으로 부하에 페이지를 기다립니다 .get()방법.

@ user227215가 말한 것처럼 특정 요소를 찾고 있듯이 WebDriverWait페이지에있는 요소를 기다리는 데 사용해야 합니다.

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

browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
try:
    myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'IdOfMyElement')))
    print "Page is ready!"
except TimeoutException:
    print "Loading took too much time!"

경고를 확인하는 데 사용했습니다. 다른 유형의 메소드를 사용하여 로케이터를 찾을 수 있습니다.

편집 1 :

webdriver페이지가 기본적으로로드 될 때까지 기다릴 것이라고 언급해야합니다 . 프레임 내부 또는 ajax 요청을로드하기를 기다리지 않습니다. 를 사용 .get('url')하면 브라우저가 페이지가 완전히로드 될 때까지 기다린 다음 코드의 다음 명령으로 이동합니다. 그러나 아약스 요청을 게시 webdriver할 때 기다리지 말고 페이지 또는 페이지의 일부가로드 될 때까지 적절한 시간을 기다려야합니다. 라는 모듈이 expected_conditions있습니다.


3
내가 얻고 있었다 변경 "(IdOfMyElement")) EC.presence_of_element_located ((By.ID) ".until (브라우저, 지연) WebDriverWait"수동 페이지의 "find_element () * 후 인수는 WebElement이 아닌, 시퀀스해야한다" 셀레늄을 python.readthedocs.org/en/latest/waits.html
취약성

2
@fragles의 의견과 David Cullen의 답변이 저에게 효과적이었습니다. 아마도이 허용되는 답변은 그에 따라 업데이트 될 수 있습니까?
Michael Ohlrogge

6
전달 browser.find_element_by_id('IdOfMyElement')하면 a NoSuchElementException가 발생합니다. 문서는 다음과 같습니다 튜플을 통과 말한다 (By.ID, 'IdOfMyElement'). 내 답변
David Cullen

2
바라건대 이것은 처음에 나에게 명확하지 않았기 때문에 다른 누군가를 도울 수 있기를 바랍니다 click(). 기다린 후에도 여전히 요소를 찾아야했습니다. 기다렸다가 나중에 찾기 요소를 수행하면 이전 대기가 여전히 처리되는 동안 요소를 찾으려고하기 때문에 셀레늄이 오류가 발생합니다 (다행스럽게도 의미가 있습니다). 결론은 WebDriverWait를 사용한 후 요소를 찾을 필요가 없다는 것입니다. 이미 객체입니다.
Ben Wilson

1
@Gopgop 와우 이것은 너무 추악한 것은 건설적인 의견이 아닙니다. 그것에 대해 못생긴 무엇입니까? 어떻게 더 나아질 수 있습니까?
Modus Tollens

72

find_element_by_id에 대한 생성자에 전달하려고 시도하면 presence_of_element_located( 허용 된 답변에 표시됨) 발생 NoSuchElementException했습니다. 나는 fragles ' comment 에서 구문을 사용해야했습니다 .

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

driver = webdriver.Firefox()
driver.get('url')
timeout = 5
try:
    element_present = EC.presence_of_element_located((By.ID, 'element_id'))
    WebDriverWait(driver, timeout).until(element_present)
except TimeoutException:
    print "Timed out waiting for page to load"

이것은 문서예제 와 일치합니다 . 다음은 By 문서에 대한 링크 입니다.


2
감사합니다! 예, 이것은 나에게도 필요했습니다. ID는 사용 가능한 유일한 속성이 아니며 전체 목록을 얻으려면 help (By)를 사용하십시오. 예 : 사용EC.presence_of_element_located((By.XPATH, "//*[@title='Check All Q1']"))
Michael Ohlrogge

그것이 나를 위해 작동하는 방식입니다! 객체 와 함께 사용할 수있는 다른 로케이터에서 확장 되는 추가 답변을 썼습니다 By.
J0ANMM

나는 항상 동일한 페이지를 다른 페이지를로드 할 수 있습니다 기대 다루는 후속 질문을 게시, 그리고했습니다 stackoverflow.com/questions/51641546/...
Liquidgenius

48

아래 3 가지 방법을 찾으십시오.

readyState

페이지 readyState 확인 (안정되지 않음) :

def page_has_loaded(self):
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
    page_state = self.driver.execute_script('return document.readyState;')
    return page_state == 'complete'

wait_for도우미 기능은 좋지만, 불행히도 click_through_to_new_page브라우저가 클릭 처리를 시작하기 전에, 우리는 이전 페이지에서 스크립트를 실행하는 관리 경쟁 조건에 개방하고, page_has_loaded단지 바로 true를 돌려줍니다.

id

새 페이지 ID와 이전 페이지 ID 비교

def page_has_loaded_id(self):
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
    try:
        new_page = browser.find_element_by_tag_name('html')
        return new_page.id != old_page.id
    except NoSuchElementException:
        return False

ID를 비교하는 것이 오래된 참조 예외를 기다리는 것만 큼 효과적이지 않을 수 있습니다.

staleness_of

staleness_of방법을 사용하여 :

@contextlib.contextmanager
def wait_for_page_load(self, timeout=10):
    self.log.debug("Waiting for page to load at {}.".format(self.driver.current_url))
    old_page = self.find_element_by_tag_name('html')
    yield
    WebDriverWait(self, timeout).until(staleness_of(old_page))

자세한 내용은 Harry의 블로그를 확인하십시오 .


self.driver.execute_script('return document.readyState;')신뢰할 수 없다고 말 합니까? 정적 파일이 새 탭에로드되기를 기다리는 내 유스 케이스에서 완벽하게 작동하는 것 같습니다 (.get () 대신 다른 탭에서 javascript를 통해 열림).
Arthur Hebert

1
@ArthurHebert 경쟁 조건으로 인해 신뢰할 수 없었습니다. 관련된 인용을 추가했습니다.
kenorb

23

David Cullen답변에서 언급했듯이 항상 다음과 같은 줄을 사용하는 것이 좋습니다.

element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)

와 함께 사용할 수있는 모든 로케이터를 찾기가 어려웠 By으므로 여기에 목록을 제공하는 것이 유용 할 것이라고 생각했습니다. Ryan Mitchell의 Python 으로 Web Scraping 에 따르면 :

ID

예제에서 사용됩니다. HTML id 속성으로 요소를 찾습니다.

CLASS_NAME

HTML 클래스 속성으로 요소를 찾는 데 사용됩니다. 이 기능이 왜 CLASS_NAME간단하지 CLASS않습니까? 이 양식을 사용하면 예약 된 메소드 인 object.CLASS Selenium의 Java 라이브러리에 문제가 발생합니다 .class. 다른 언어간에 셀레늄 구문을 일관성있게 유지하기 위해 CLASS_NAME대신 사용되었습니다.

CSS_SELECTOR

사용하여, 자신의 클래스 ID 또는 태그 이름으로 요소를 찾아 #idName, .className, tagName규칙을.

LINK_TEXT

포함 된 텍스트로 HTML 태그를 찾습니다. 예를 들어을 사용하여 "다음"이라는 링크를 선택할 수 있습니다 (By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

와 비슷 LINK_TEXT하지만 부분 문자열에서 일치합니다.

NAME

이름 속성으로 HTML 태그를 찾습니다. HTML 양식에 편리합니다.

TAG_NAME

태그 이름으로 HTML 태그를 찾습니다.

XPATH

XPath 표현식을 사용하여 일치하는 요소를 선택하십시오.


5
에 의해 문서는 로케이터로 사용되는 특성이 나열되어 있습니다.
David Cullen

1
그것이 내가 찾던 것이었다! 감사! 글쎄, 이제 구글 이이 질문에 나를 보내지 만 공식 문서로는 보내지 않았기 때문에 찾기가 더 쉬워졌습니다.
J0ANMM

이 책에서 인용 해 주셔서 감사합니다. 문서보다 훨씬 명확합니다.
ZygD


11

참고로 100 번 아래로 스크롤하는 대신 DOM에 더 이상 수정 사항이 없는지 확인할 수 있습니다 (페이지 하단이 AJAX 지연로드 인 경우)

def scrollDown(driver, value):
    driver.execute_script("window.scrollBy(0,"+str(value)+")")

# Scroll down the page
def scrollDownAllTheWay(driver):
    old_page = driver.page_source
    while True:
        logging.debug("Scrolling loop")
        for i in range(2):
            scrollDown(driver, 500)
            time.sleep(2)
        new_page = driver.page_source
        if new_page != old_page:
            old_page = new_page
        else:
            break
    return True

유용합니다. 그러나 500은 무엇을 의미합니까? 페이지 끝 부분에 도달하기에 충분히 큰가요?
Moondra

페이지를 스크롤해야하는 양입니다. 가능한 한 높게 설정해야합니다. 난 그냥 AJAX 요소 게으른로드 될 때까지 그것을 다시로드 할 필요 박차를 가하고, 바닥까지 페이지 스크롤을하게하기 때문에이 숫자는 페이지 다시 나를 위해 충분한 것을 발견
raffaem

이것은 gitlab의 문제에 대한 모든 의견이 완전히로드되도록 할 때 도움이됩니다.
bgStack15

7

당신이 시도 했습니까 driver.implicitly_wait. 드라이버 설정과 같으므로 세션에서 한 번만 호출하면 기본적으로 드라이버가 각 명령을 실행할 수있을 때까지 주어진 시간 동안 기다리도록 지시합니다.

driver = webdriver.Chrome()
driver.implicitly_wait(10)

따라서 대기 시간을 10 초로 설정하면 가능한 빨리 명령을 실행하고 포기하기 전에 10 초 동안 대기합니다. 비슷한 스크롤 다운 시나리오에서 이것을 사용했기 때문에 귀하의 경우에는 왜 작동하지 않는지 알 수 없습니다. 이것이 도움이 되길 바랍니다.

이 답변을 수정하려면 새 텍스트를 추가해야합니다. 에 소문자 'w'를 사용해야합니다 implicitly_wait.


암시 적 대기와 webdriverwait의 차이점은 무엇입니까?
song0089

4

While 루프에 WebDriverWait를 넣고 예외를 잡는 것은 어떻습니까.

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
while True:
    try:
        WebDriverWait(browser, delay).until(EC.presence_of_element_located(browser.find_element_by_id('IdOfMyElement')))
        print "Page is ready!"
        break # it will break from the loop once the specific element will be present. 
    except TimeoutException:
        print "Loading took too much time!-Try again"

루프가 필요하지 않습니까?
코리 골드버그

4

여기서는 간단한 양식을 사용하여 수행했습니다.

from selenium import webdriver
browser = webdriver.Firefox()
browser.get("url")
searchTxt=''
while not searchTxt:
    try:    
      searchTxt=browser.find_element_by_name('NAME OF ELEMENT')
      searchTxt.send_keys("USERNAME")
    except:continue

1

이 기능으로 매우 간단하게 할 수 있습니다.

def page_is_loading(driver):
    while True:
        x = driver.execute_script("return document.readyState")
        if x == "complete":
            return True
        else:
            yield False

페이지로드가 완료된 후 무언가를 원할 때 다음을 사용할 수 있습니다.

Driver = webdriver.Firefox(options=Options, executable_path='geckodriver.exe')
Driver.get("https://www.google.com/")

while not page_is_loading(Driver):
    continue

Driver.execute_script("alert('page is loaded')")
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.