如何等待,直到页面加载与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
,.className
,tagName
约定,通过类,标识或标记名称查找元素。
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