簡體   English   中英

等到頁面用 Selenium WebDriver for Python 加載

[英]Wait until page is loaded with Selenium WebDriver for Python

我想抓取無限滾動實現的頁面的所有數據。 以下python代碼有效。

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

這意味着每次我向下滾動到底部時,我都需要等待 5 秒,這通常足以讓頁面完成加載新生成的內容。 但是,這可能沒有時間效率。 頁面可能會在 5 秒內完成加載新內容。 每次向下滾動時,如何檢測頁面是否完成加載新內容? 如果我能檢測到這一點,我可以在知道頁面完成加載后再次向下滾動以查看更多內容。 這樣更省時。

默認情況下, 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') ,您的瀏覽器將等待頁面完全加載,然后轉到代碼中的下一個命令。 但是當您發布 ajax 請求時, webdriver不會等待,您有責任等待適當的時間來加載頁面或頁面的一部分; 所以有一個名為expected_conditions的模塊。

試圖通過find_element_by_id給構造為presence_of_element_located (如圖中接受的答案)引起NoSuchElementException被升高。 我不得不在fragles評論中使用語法:

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 文檔的鏈接。

找到以下3種方法:

就緒狀態

檢查頁面 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 的博客

正如David Cullen回答中提到的,我一直看到建議使用如下一行:

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

我很難在某處找到可以與By一起使用的所有可能的定位器,所以我認為在這里提供列表會很有用。 根據 Ryan Mitchell 的Web Scraping with Python的說法:

ID

示例中使用; 通過 HTML id 屬性查找元素

CLASS_NAME

用於通過元素的 HTML 類屬性查找元素。 為什么這個函數CLASS_NAME不是簡單的CLASS 使用表單object.CLASS會給 Selenium 的 Java 庫帶來問題,其中.class是一個保留方法。 為了使不同語言之間的 Selenium 語法保持一致,改為使用CLASS_NAME

CSS_SELECTOR

使用#idName.classNametagName約定按類、id 或標簽名稱查找元素。

LINK_TEXT

根據它們包含的文本查找 HTML 標簽。 例如,可以使用(By.LINK_TEXT, "Next")選擇(By.LINK_TEXT, "Next")

PARTIAL_LINK_TEXT

類似於LINK_TEXT ,但匹配部分字符串。

NAME

按名稱屬性查找 HTML 標簽。 這對於 HTML 表單很方便。

TAG_NAME

按標簽名稱查找 HTML 標簽。

XPATH

使用 XPath 表達式 ... 來選擇匹配的元素。

來自selenium/webdriver/support/wait.py

driver = ...
from selenium.webdriver.support.wait import WebDriverWait
element = WebDriverWait(driver, 10).until(
    lambda x: x.find_element_by_id("someId"))

在旁注中,您可以檢查是否沒有對 DOM 進行更多修改,而不是向下滾動 100 次(我們是在頁面底部進行 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

你有沒有試過driver.implicitly_wait 它就像驅動程序的設置,因此您只在會話中調用它一次,它基本上告訴驅動程序等待給定的時間,直到可以執行每個命令。

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

因此,如果您將等待時間設置為 10 秒,它將盡快執行命令,等待 10 秒后它會放棄。 我在類似的向下滾動場景中使用過它,所以我不明白為什么它在你的情況下不起作用。 希望這是有幫助的。

為了能夠修復這個答案,我必須添加新文本。 確保在implicitly_wait使用小寫的“w”。

在這里,我使用了一個相當簡單的形式:

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

ajax頁面不斷加載數據的解決方案。 所述的預覽方法不起作用。 我們可以做的是獲取頁面 dom 並對其進行哈希處理,然后在增量時間內將舊的和新的哈希值進行比較。

import time
from selenium import webdriver

def page_has_loaded(driver, sleep_time = 2):
    '''
    Waits for page to completely load by comparing current page hash values.
    '''

    def get_page_hash(driver):
        '''
        Returns html dom hash
        '''
        # can find element by either 'html' tag or by the html 'root' id
        dom = driver.find_element_by_tag_name('html').get_attribute('innerHTML')
        # dom = driver.find_element_by_id('root').get_attribute('innerHTML')
        dom_hash = hash(dom.encode('utf-8'))
        return dom_hash

    page_hash = 'empty'
    page_hash_new = ''
    
    # comparing old and new page DOM hash together to verify the page is fully loaded
    while page_hash != page_hash_new: 
        page_hash = get_page_hash(driver)
        time.sleep(sleep_time)
        page_hash_new = get_page_hash(driver)
        print('<page_has_loaded> - page not loaded')

    print('<page_has_loaded> - page loaded: {}'.format(driver.current_url))

如何將 WebDriverWait 放入 While 循環並捕獲異常。

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"

您可以通過此功能非常簡單地做到這一點:

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')")

在代碼中使用它:

from selenium import webdriver

driver = webdriver.Firefox() # or Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.......")

或者,如果您正在尋找特定標簽,則可以使用此代碼:

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() #or Chrome()
driver.get("http://www.......")
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "tag_id"))
    )
finally:
    driver.quit()

這里的答案非常好。 等待XPATH快速示例。

# wait for sizes to load - 2s timeout
try:
    WebDriverWait(driver, 2).until(expected_conditions.presence_of_element_located(
        (By.XPATH, "//div[@id='stockSizes']//a")))
except TimeoutException:
    pass

我努力讓這個工作,因為它沒有按預期對我有用。 任何仍在努力使其工作的人都可以檢查這個。

我想等待一個元素出現在網頁上,然后再繼續我的操作。

我們可以使用 WebDriverWait(driver, 10, 1).until(),但問題是直到()期望一個函數,它可以每 1 秒執行一段提供的超時時間(在我們的例子中為 10)。 所以保持它像下面一樣對我有用。

element_found = wait_for_element.until(lambda x: x.find_element_by_class_name("MY_ELEMENT_CLASS_NAME").is_displayed())

這是直到()在幕后做的事情

def until(self, method, message=''):
        """Calls the method provided with the driver as an argument until the \
        return value is not False."""
        screen = None
        stacktrace = None

        end_time = time.time() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, 'screen', None)
                stacktrace = getattr(exc, 'stacktrace', None)
            time.sleep(self._poll)
            if time.time() > end_time:
                break
        raise TimeoutException(message, screen, stacktrace)

如果您嘗試滾動並查找頁面上的所有項目。 您可以考慮使用以下方法。 這是這里其他人提到的幾種方法的組合。 它為我完成了工作:

while True:
    try:
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        driver.implicitly_wait(30)
        time.sleep(4)
        elem1 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
        len_elem_1 = len(elem1)
        print(f"A list Length {len_elem_1}")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        driver.implicitly_wait(30)
        time.sleep(4)
        elem2 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
        len_elem_2 = len(elem2)
        print(f"B list Length {len_elem_2}")
        if len_elem_1 == len_elem_2:
            print(f"final length = {len_elem_1}")
            break
    except TimeoutException:
            print("Loading took too much time!")

selenium 無法檢測頁面何時完全加載,但 javascript 可以。 我建議你試試這個。

from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, 100).until(lambda driver: driver.execute_script('return document.readyState') == 'complete')

這將執行 javascript 代碼而不是使用 python,因為 javascript 可以檢測頁面何時完全加載,它將顯示“完成”。 此代碼意味着在 100 秒內,繼續嘗試 document.readyState 直到完整顯示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM