如何等待,直到页面加载与seleniumfor Python?

我想抓取一个由无限滚动实现的页面的所有数据。 下面的python代码工作。

for i=1: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!" 

我用它来检查警报。 您可以使用任何其他types的方法来查找定位器。

编辑1:

我应该提到, webdriver默认会等待页面加载。 它不等待加载内部框架或Ajax请求。 这意味着当您使用.get('url') ,浏览器将等待页面完全加载,然后转到代码中的下一个命令。 但是,当您发布ajax请求时, webdriver不会等待,并且您有责任等待适当的时间来加载页面或部分页面; 所以有一个名为expected_conditions的模块。

尝试将find_element_by_id传递给find_element_by_id的构造函数(如接受的答案中所示),导致引发NoSuchElementException find_element_by_id 。 我不得不在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' 

比较新的页面ID和旧的:

 def page_has_loaded2(): 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方法:

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

我很难find所有可能的与By语法一起使用的定位符,所以我认为在这里提供这个列表是很有用的。 根据瑞恩·米切尔(Ryan Mitchell) 用Python进行的Web Scraping

ID

在例子中使用; 通过他们的HTML id属性查找元素

CLASS_NAME

用于通过其HTML类属性查找元素。 为什么这个函数CLASS_NAME不是简单的CLASS ? 使用表单object.CLASS会为Selenium的Java库创build问题,其中.class是一个保留的方法。 为了保持Selenium语法在不同语言之间一致,改为使用CLASS_NAME

CSS_SELECTOR

使用#idName.classNametagName约定,通过类,标识或标记名称查找元素。

LINK_TEXT

通过所包含的文本查找HTML标记。 例如,可以使用(By.LINK_TEXT, "Next")select“Next”的链接。

PARTIAL_LINK_TEXT

LINK_TEXT类似,但匹配部分string。

NAME

通过名称属性查找HTML标签。 这对于HTML表单来说非常方便。

TAG_NAME

通过标签名称填充HTML标签。

XPATH

使用XPathexpression式…来select匹配的元素。

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

如何把WebDriverWait放入While循环并捕获exception。

 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" 

在一个侧面说明,而不是向下滚动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