等到页面加载Selenium WebDriver for Python

时间:2014-10-25 20:14:31

标签: python selenium

我想要抓取无限滚动实现的页面的所有数据。以下python代码可以正常工作。

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

这意味着每次向下滚动到底部时,我需要等待5秒钟,这通常足以让页面完成加载新生成的内容。但是,这可能不是时间效率。页面可能会在5秒内完成加载新内容。每次向下滚动时,如何检测页面是否已完成加载新内容?如果我能检测到这一点,一旦我知道页面加载完毕,我可以再次向下滚动以查看更多内容。这样更有时间效率。

12 个答案:

答案 0 :(得分:148)

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的模块。

答案 1 :(得分:55)

尝试将find_element_by_id传递给presence_of_element_located的构造函数(如accepted answer所示),导致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"

这匹配example in the documentation。这是指向documentation for By的链接。

答案 2 :(得分:30)

查找以下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对竞争条件开放,我们设法在浏览器开始处理点击之前在旧页面中执行脚本,并且{ {1}}直接返回true。

page_has_loaded

将新页面ID与旧页面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

使用staleness_of方法:

staleness_of

有关详细信息,请查看Harry's blog

答案 3 :(得分:16)

来自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"))

答案 4 :(得分:14)

正如answer from 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按类,ID或标记名称查找元素,   .classNametagName惯例。

     

<强> LINK_TEXT

     

按照包含的文字查找HTML标记。例如,一个链接   说“下一步”可以使用(By.LINK_TEXT, "Next")选择。

     

<强> PARTIAL_LINK_TEXT

     

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

     

<强> NAME

     

按名称属性查找HTML标记。这对HTML表单很方便。

     

<强> TAG_NAME

     

按标记名称填写HTML标记。

     

<强> XPATH

     

使用XPath表达式...来选择匹配的元素。

答案 5 :(得分:7)

在旁注中,您可以检查是否没有对DOM进行更多修改(我们在页面底部是AJAX延迟加载的情况下),而不是向下滚动100次。

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

答案 6 :(得分:3)

在这里,我使用了一种非常简单的形式:

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

答案 7 :(得分:1)

您是否尝试过driver.implicitly_wait。它就像是驱动程序的设置,所以你只在会话中调用它一次,它基本上告诉驱动程序等待给定的时间,直到每个命令都可以执行。

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

因此,如果您设置10秒的等待时间,它将尽快执行命令,等待10秒后才放弃。我已经在类似的向下滚动场景中使用了这个,所以我不明白为什么它不会在你的情况下工作。希望这有用:)

答案 8 :(得分:1)

连续加载数据的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))

答案 9 :(得分:1)

在代码中使用它:

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()

答案 10 :(得分:1)

这里的答案非常好。等待 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

答案 11 :(得分:0)

您可以通过此功能非常简单地完成此操作:

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