等到页面加载Selenium WebDriver for Python

apo*_*gne 142 python selenium execute-script

我想刮掉由无限滚动实现的页面的所有数据.以下python代码有效.

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

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

Zei*_*zar 179

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!"
Run Code Online (Sandbox Code Playgroud)

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

编辑1:

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

  • 传递`browser.find_element_by_id('IdOfMyElement')`导致引发`NoSuchElementException`.[documentation](http://selenium-python.readthedocs.io/waits.html#explicit-waits)说要传递一个如下所示的元组:`(By.ID,'IdOfMyElement')`.见[我的回答](http://stackoverflow.com/a/37303115/3657941) (5认同)
  • 我是越来越 "find_element()*后的参数必须是一个序列,不WebElement" 改为 "WebDriverWait(浏览器,延迟).直到(EC.presence_of_element_located((By.ID, "IdOfMyElement")))" 看到手册HTTP: //selenium-python.readthedocs.org/en/latest/waits.html (3认同)
  • @fragles的评论和David Cullen的回答对我有用。也许这个接受的答案可以相应地更新? (2认同)
  • 希望这可以帮助其他人,因为我最初并不清楚:WebDriverWait 实际上会返回一个 Web 对象,然后您可以对其执行操作(例如`click()`)、读取文本等。我​​在错误的印象是它只是引起了等待,之后您仍然必须找到该元素。如果您等待,然后查找元素,selenium 将出错,因为它会在旧等待仍在处理时尝试查找元素(希望这是有道理的)。底线是,在使用 WebDriverWait 之后您不需要找到该元素——它已经是一个对象。 (2认同)

Dav*_*len 58

试图传递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"
Run Code Online (Sandbox Code Playgroud)

这与文档中的示例相匹配.以下是By文档链接.

  • 谢谢!是的,我也需要这样做。ID不是唯一可以使用的属性,要获取完整列表,请使用help(By)。例如,我使用了`EC.presence_of_element_located((By.XPATH,“ // * [@ title ='Check All Q1']”)))` (2认同)

ken*_*orb 37

找到以下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'
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

比较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))
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请查看Harry的博客.


Car*_*arl 17

来自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"))
Run Code Online (Sandbox Code Playgroud)


J0A*_*NMM 15

正如David Cullen回答中提到的,我看到总是建议使用如下所示的行:

element_present = EC.presence_of_element_located((By.ID, 'element_id'))
    WebDriverWait(driver, timeout).until(element_present)
Run Code Online (Sandbox Code Playgroud)

我很难找到可以与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, .className,tagName约定.

LINK_TEXT

通过它们包含的文本查找HTML标记.例如,可以使用选择"下一步"的链接(By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

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

NAME

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

TAG_NAME

按标签名称填充HTML标记.

XPATH

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

  • [By]的文档(http://selenium-python.readthedocs.io/api.html?highlight=#module-selenium.webdriver.common.by)列出了可用作定位器的属性. (4认同)

see*_*spi 13

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

driver = webdriver.Chrome()
driver.implicitly_wait(10)
Run Code Online (Sandbox Code Playgroud)

因此,如果您将等待时间设置为 10 秒,它将尽快执行该命令,等待 10 秒后放弃。我在类似的向下滚动场景中使用过它,所以我不明白为什么它在你的情况下不起作用。希望这是有帮助的。

为了能够修复这个答案,我必须添加新文本。确保在implicitly_wait.


rtr*_*trt 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
Run Code Online (Sandbox Code Playgroud)


ahm*_*lek 7

在这里,我使用了一个相当简单的形式:

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
Run Code Online (Sandbox Code Playgroud)


Pou*_*sel 6

你可以通过这个函数非常简单地做到这一点:

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

当您想在页面加载完成后执行某些操作时,您可以使用:

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')")
Run Code Online (Sandbox Code Playgroud)

  • 这是纯粹的 CPU 阻塞脚本。 (3认同)
  • 为正确性点赞。最优性是一个单独的问题,但这通常是有效的。 (3认同)

SoR*_*bby 6

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))
Run Code Online (Sandbox Code Playgroud)


小智 6

selenium 无法检测页面何时完全加载或未加载,但 javascript 可以。我建议你试试这个。

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

这将执行 javascript 代码而不是使用 python,因为 javascript 可以检测页面何时完全加载,它会显示“完成”。此代码意味着在 100 秒内,继续尝试 document.readyState 直到显示完成。


小智 5

如何将 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"
Run Code Online (Sandbox Code Playgroud)

  • 你不需要循环? (2认同)

mam*_*mal 5

在代码中使用它:

from selenium import webdriver

driver = webdriver.Firefox() # or Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.......")
Run Code Online (Sandbox Code Playgroud)

或者,如果您正在寻找特定标签,您可以使用此代码:

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()
Run Code Online (Sandbox Code Playgroud)