【WebDriver API】设置元素等待

如今大多数web应用程序使用AJAX技术。当浏览器在加载页面时,页面上的元素可能并不是同时被加载完成的,这给元素的定位增加了困难。如果因为在加载某个元素时延迟而造成ElementNotVisibleException的情况出现,那么就会降低自动化脚本的稳定性,我们可以通过设置元素等待改善这种问题造成的不稳定。

WebDriver提供了两种类型的等待:显式等待和隐式等待。

1.显示等待

显示等待使WebDriver等待某个条件成立时继续执行,否则在达到最长时抛弃超时异常(TimeoutException)。

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
from time import sleep
driver=webdriver.Firefox()
driver.get("http://www.baidu.com")
element=WebDriverWait(driver,5,0.5).until(EC.presence_of_element_located((By.ID,"kw")))
element.send_keys('selenium')
sleep(3)
driver.quit()

WebDriverWait类是由WebDriver提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。具体格式如下:

WebDriverWaitJ(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)

driver:浏览器驱动。

timeout:最大超时时间,默认以秒为单位。

poll_frequency:检测的间隔(步长)时间,默认为0.5S。

ignored_exceptions:超过后的异常信息,默认情况下抛NoSuchElementException异常。

WebDriverWait()一般由until()或until_not()方法配合使用,下面是until()和until_not()方法的说明 。

until(method,message='')

调用该方法提供的驱动程序作为一个参数,直到返回值为True。

until_not(method,message='')

调用该方法提供的驱动程序作为一个参数,直到返回值为False。

在本例中,通过as关键字将expected_conditions生命名为EC,并调用presence_of_element_located()方法判断元素是否存在。

expected_conditions类所提供的预期条件判断的方法。

方法 说明
title_is 判断当前页面的标题是否等于预期
title_contains 判断当前页面的标题是否包含预期字符串
presence_of_element_located 判断元素是否被加在DOM树里,并不代表该元素一定可见
visibility_of_element_located 判断元素是否可见(可见代表元素非隐藏,并且元素的宽和高都不等于0)
visibility_of 与上一个方法作用相同,只是上一个方法参数为定位,该方法接受的参数为定位后的元素
presence_of_all_elements_located 判断是否至少有一个元素存在于DOM树中。例如,在个页面中有n个元素的class为“wp”,那么只要有一个存在或返回True
text_to_be_present_in_element 判断某个元素中的text是否包含了预期的字符串
text_to_be_present_in_element_value 判断某个元素的value属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it 判断该表单是否可以切换进去,如果可以,返回True并且switch进去,否则返回False
invisibility_of_element_located 判断某个元素是否不存在与DOM数或不可见
element_to_be_clickable 判断元素是否可见并且是可以点击的
staleness_of 等到一个元素从DOM树中移除
element_to_be_selected 判断某个元素是否被选中,一般用在下拉列表
element_selection_state_to_be 判断某个元素的选中状态是否符合预期
element_located_selection_state_to_be 与上一个方法作用相同,只是上一个方法参数为定位后的元素,该方法接收的参数为定位
alert_is_present 判断页面上是否存在alert

除expected_conditions所提供的丰富的预期条件判断方法外,还可以使用前面学过的is_displayed()方法来判断元素是否可见。

from selenium import webdriver
from time import sleep,ctime
driver=webdriver.Firefox()
driver.get("http://www.baidu.com")

print(ctime())
for i in range(10):
    try:
        el=driver.find_element_by_id("kw22")
        if el.is_displayed():
            break
    except:pass
    sleep(1)
else:
    print("time out")
driver.close()
print(ctime())
driver.quit()

输出结果:

说明:通过for循环10次,每次循环判断元素的is_displayed()状态是否为True:如果为True,则break跳出循环;否则sleep(1)后继续循环判断,直到10此循环结束后,打印“time out”信息。

2.隐式等待

隐式登陆是通过一定的时长等待页面上某元素加载完成。如果超出了设置的时长元素还没有被加载,则抛出NoSuchElementException异常。WebDriver提供了implicitly_wait()方法来实现隐式等待,默认设置为0.它的用法相对来说要简单很多。

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from time import sleep,ctime
driver=webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
try:
    print(ctime())
    driver.find_element_by_id("kw22").send_keys('selenium')
except NoSuchElementException as e:
    print(e)
finally:
    print(ctime())
    driver(quit())

输出结果:

说明:implicitly_wait()默认参数的单位为秒,本例中设置等待时长为10秒。首先这10秒并非一个固定的等待时间,它并不影响脚本的执行速度。其次,它并不针对页面上的某一元素进行等待。当脚本执行到某个元素定位时,如果元素可以定位,则继续执行;如果元素定位不到,则它将以轮询的方式不断地判断元素是否被定位到。假设在第6秒定位到了元素则继续执行,若直到超出设置时长(10秒)还没有定位到元素,则抛出异常。

在上面的例子中,显然百度输入框的定位id=kw22是有误的,通过打印的两个时间可以看出,当执行对百度输入框的操作时,超过了10秒的等待。

3.sleep休眠方法

有时候我们希望脚本在执行到某一位置时做固定时间的休眠,尤其是在脚本调试过程中。这时可以使用sleep()方法,需要说明的是,sleep()方法有Python的time模块提供。

from selenium import webdriver
from time import sleep
driver=webdriver.Firefox()
driver.get("http://www.baidu.com")
sleep(2)
driver.find_element_by_id("kw").send_keys("webdriver")
driver.find_element_by_id("su").click()
sleep(3)
driver(quit())

当执行到sleep()方法时会固定休眠一定的时长,然后再继续执行。sleep()方法默认参数以秒为单位,如果设置时长小于1秒,则可以用小数表示,如sleep(0.5)表示休眠0.5秒。

4.定位一组元素

WebDriver还提供了与之对应的8种用于定位一组元素的方法。

find_elements_by_id()

find_elements_by_name()

find_elements_by_class_name()

find_elements_by_tag_name()

find_elements_by_link_text()

find_elements_by_partial_link_text()

find_elements_by_xpath()

find_elements_by_css_selector()

定位一组元素一般用于以下场景:

  • 批量操作元素,例如勾选页面上所有的复选框。
  • 先获取一组元素,再从这组对象中过虑出需要操作的元素。例如定位出页面上所有的checkbox,然后选择其中的一个进行操作。

checkbox.html

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<title>Checkbox</title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.js"></script>
</head>

    <body>
        <h3>checkbox</h3>
        <div class="well">
            <form class="form-horizontal">
                <div class="control-group">
                    <label class="control-label" for="c1">checkbox1</label>
                    <div class="controls">
                        <input type="checkbox" id="c1" />
                    </div>
                </div>
                <div class="control-group">
                    <label class="control-label" for="c2">checkbox1</label>
                    <div class="controls">
                        <input type="checkbox" id="c2" />
                    </div>
                </div>
                <div class="control-group">
                    <label class="control-label" for="c3">checkbox1</label>
                    <div class="controls">
                        <input type="checkbox" id="c3" />
                    </div>
                </div>
            </form>
        </div>
    </body>
</html>

checkbox.py

from selenium import webdriver
import os,time

driver=webdriver.Firefox()
file_path='file:///' + os.path.abspath('checkbox.html')
driver.get(file_path)
#选择页面上所有的tag name为input的元素
inputs=driver.find_elements_by_tag_name('input')
#然后从中过虑出type为checkbox的元素,单击勾选
for i in inputs:
    if i.get_attribute('type')=='checkbox':
        i.click()
        time.sleep(1)
driver.quit()

说明:通过tag name的定位方式很难定位到单个元素,因为元素标签重名的概率很高,因而在定位一组元素时,这种方法就派上用场了。在上面的例子中先通过find_elements_by_tag_name()找到一组标签名为input的元素。然后通过for循环进行遍历,在遍历过程中,通过get_attribute()方法获取元素的type属性是否为“checkbox”,如果为“chenkbox”,就认为这个元素是一个复选框,对其进行勾选操作。

通过浏览器打开一个本地的html文件,所以需要用到Python的os模块,path.abspath()方法用于获取当前路径下的文件。

我们还可以使用XPath或CSS来直接判断属性值,从而进行单击操作。

from selenium import webdriver
import os,time

driver=webdriver.Firefox()
file_path='file:///' + os.path.abspath('checkbox.html')
driver.get(file_path)

#通过XPath找到type=checkbox的元素
#checkboxes=driver.find_elements_by_xpath('//input[@type="checkbox"]')
#通过CSS找到type=checkbox的元素
checkboxes=driver.find_elements_by_css_selector('input[type="checkbox"]')
#然后从中过虑出type为checkbox的元素,单击勾选
for checkbox in checkboxes:
    checkbox.click()
    time.sleep(1)
#打印当前页面上type为checkbox的个数
print(len(checkboxes))
#把页面上最后1个chenkbox的钩给去掉
driver.find_elements_by_css_selector("input[type=checkbox]").pop().click()
driver.quit()

说明:通过XPath或CSS来查找一组元素时,省去了判断步骤。因为定位方法已经做了判断,只需循环对这一组元素进行勾选即可。len()方法用于获取列表中的一个元素的个数,通过print()打印出计算的结果。pop()方法用于获取列表中的一个元素(默认最后一个元素),并且返回该元素的值。如果只想勾选一组元素中的某一个该如何操作?

pop()或pop(-1):默认获取一组元素中的最后一个。

pop(0):默认获取一组元素中的第一个。

pop(1):默认获取一组元素中的第二个。

......

这样就可以操作这一组元素中的任意一个元素了,只需数一数需操作的元素是这一组中的第几个。

发布了23 篇原创文章 · 获赞 6 · 访问量 4756

猜你喜欢

转载自blog.csdn.net/w68688686/article/details/103616417