目录

一、元素等待(70节44min)

1.强制等待

2.隐性等待

3.显性等待

二、三大切换

1.窗口切换

2.iframe切换

3.alert弹框切换

 

正文

一、元素等待

元素等待的3种方式:

1.强制等待:time.sleep

2.隐性等待

3.显性等待(重点)

 

灵魂一问:为什么要进行元素等待?

CPU的运行速度远远大于网页的页面加载速度,会导致定位不到元素,所以要进行元素等待。

背景实操:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
e = driver.find_element_by_id("kw")
e.send_keys("python")

运行结果:在输入框中有输入python,但是并没有查询结果,因为没有点击“百度一下”

 

 下面添加“百度一下”的点击操作

先定位百度一下:id=su

 

from selenium import webdriver

driver = webdriver.Chrome()
#打开百度
driver.get("http://www.baidu.com")
#定位输入框,用i属性
e = driver.find_element_by_id("kw")
#输入框中输入python
e.send_keys("python")

#定位 百度一下
e = driver.find_element_by_id("su")
e.click()

在“百度一下”以后的页面中定位一个link_text的超链接文本:www.python.org/

 

 

from selenium import webdriver

driver = webdriver.Chrome()
#打开百度
driver.get("http://www.baidu.com")
#定位输入框,用i属性
e = driver.find_element_by_id("kw")
#输入框中输入python
e.send_keys("python")

#定位 百度一下
e = driver.find_element_by_id("su")
e.click()
#定位 text
e = driver.find_element_by_link_text("www.python.org/")

运行结果:定位不到该元素

当进行“百度一下”的操作,有个网页加载的过程,页面渲染可能快,可能慢,(python的运行程序很快)所以进行text定位的时候,会出现定位不到的情况。所以:加入元素等待时间。

要等页面加载完成,才能执行e = driver.find_element_by_link_text("www.python.org/")

 

1.强制等待

上面的例子中加入强制等待时间(黄底代码)

import time
from selenium import webdriver

driver = webdriver.Chrome()
#打开百度
driver.get("http://www.baidu.com")
#定位输入框,用i属性
e = driver.find_element_by_id("kw")
#输入框中输入python
e.send_keys("python")

#定位 百度一下
e = driver.find_element_by_id("su")
e.click()

#强制等待 time.sleep(
2) #定位 text e = driver.find_element_by_link_text("www.python.org/")

强制等待总结:

方法:time.sleep(),单位是秒
使用方法:time.sleep(X),等待X秒后,进行下一步操作。无论条件成立与否,都要等待到时间截至,才能进行下一步操作
缺点:不能准确把握需要等待的时间(有时操作还未完成,等待就结束了,导致报错;有时操作已经完成了,但等待时间还没有到,浪费时间),如果在用例中大量使用,会浪费不必 要的等待时间,影响测试用例的执行效率。-------时间不好控制(不够灵活)
优点:使用简单,可以在调试时使用。

 

智能等待:给定超时时间,在超时时间内,能够找到元素,就直接返回元素。如果在给你的超时时间内没有定位到元素,直接报错找不到元素。

举例:给定超时时间20s,在第5s的时候可以找到元素,就直接返回。超过20s没有找到,就直接报错找不到元素。

 

2.隐性等待-----全局作用域

打开浏览器后,设置隐性等待的超时时间:driver.implicitly_wait()。设置这一次,所有的元素查找都能够生效。比如,设置了隐性等待的超时时间为5s,查找第一个元素会等待5s,查找第二个也会等待5s,.。。。。。。。(超过超时时间找不到元素,报错,noSuchElementException)

from selenium import webdriver

driver = webdriver.Chrome()
#打开百度
driver.get("http://www.baidu.com")
driver.implicitly_wait(2)

#定位输入框,用i属性
e = driver.find_element_by_id("kw")
#输入框中输入python
e.send_keys("python")

#定位 百度一下
e = driver.find_element_by_id("su")
e.click()

#定位 text
e = driver.find_element_by_link_text("www.python.org/")

隐性等待总结:

隐形等待:只能等待元素加载,查找元素是否存在。------只能等待元素被查找到
全局作用域:会话期间全局设置一次,所有元素查询都通用
使用方法:设置超时时间:driver.implicitly_wait(X),在X时间内,页面加载完成,进行下一步操作。超过超时时间,报NoSuchElementException
                  设置了一个最长等待时间,如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间结束,然后执行下一步操作。
缺点:使用隐式等待,程序会一直等待整个页面加载完成,才会执行下一步操作;只能等待元素被查到,其他的等待,无法使用。----------不够灵活
优点:隐性等待对整个driver的周期都起作用,所以只要设置一次即可。

来看下   implicitly_wait()函数的源码:只能等待一个元素,其他的它等不了。---如果要去等元素被点击、元素可见、等待窗口被打开等等,隐性等待就不起作用了。

举例:一个元素能够被找到,不一定该元素能够点击。

 

 

3.显性等待  (71节  13min)-----每次等待都要单独设置,这一点同强制等待一样

显性等待,就是解决隐性等待解决不了的问题。

 ---等待某个元素被点击

---等待某个元素可见

---等待某个窗口被打开

 

显性等待的步骤

设置定时器wait

wait = WebDriverWait()

WebDriverWait是selenium.webdriver.support.wait中的一个类

下面看下WebDriverWait类的源码:

传入的参数有driver(浏览器对象)、timeout(超时时间)、poll_frequency(轮询时间,每隔多久查询一次,比如设置1s,每隔1s查询一次,第1s查询,查到就返回,查不到等待下一次即第2s再查询一次,以此类推)

 

 

②设置满足的条件:wait.until()

until()中设置一个预期条件:expected_conditions <------------(from selenium.webdriver.support  import  expected_conditions)

expected_conditions下,封装的条件很多,下面介绍几个常用的条件:

1).等待元素出现:presence_of_element_located

    等待元素出现,到底等待那个元素出现呢???----传入locator参数

presence_of_element_located()方法的源码:

 

 locator是个元组,元组里面是定位元素的方式,加表达式

locator提前写好:locator = ("xpath",'//*[text() = "www.python.org/"]')

完整语句:

wait.until(expected_conditions.presence_of_element_located(locator=("xpath",'//*[text()= "www.python.org/"]')))#locator是个元组

代码:

"""显性等待"""

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

#初始化一个浏览器对象:driver
driver = webdriver.Chrome()
#打开浏览器
driver.get("http://www.baidu.com")

#定位百度输入框
e = driver.find_element_by_id("kw")
e.send_keys("柠檬班")

#定位百度一下,查询python
driver.find_element_by_id("su").click()

#设置显性等待时间wait
wait = WebDriverWait(driver,20,poll_frequency=0.5)#poll_frequency的默认值就是0.5

#设置满足的条件
locator = ('xpath','//*[text() = "lemon.ke.qq.com/"]')
elem = wait.until(expected_conditions.presence_of_element_located(locator=locator))#locator是个元组
print(elem.text)  #结果打印:lemon.ke.qq.com/

 

2)元素是否可见:visibility_of_element_located(locator = locator)

3)元素是否被点击:element_to_be_clickable(locator=locator)

什么场景用什么方式?

需要点击某个元素:用element_to_be_clickable

需要查看某个元素:优先选用visibility_of_element_located

元素可见用不了,再换用presence_of_element_located

 

显性等待方法----封装

def wait_element_clikable(locator,driver,timeout = 20,poll = 0.5):
    """等待元素被点击"""
    wait = WebDriverWait(driver, timeout=timeout, poll_frequency=poll)
    elem = wait.until(expected_conditions.element_to_be_clickable(locator=locator))
    return elem

def wait_elemet_presence(locator,driver,timeout = 20,poll = 0.5):
    """等待元素出现"""
    wait = WebDriverWait(driver, timeout=timeout, poll_frequency=poll)
    elem = wait.until(expected_conditions.presence_of_element_located(locator=locator))
    return elem

def wait_elemet_visiable(locator,driver,timeout = 20,poll = 0.5):
    """等待元素可见"""
    wait = WebDriverWait(driver, timeout=timeout, poll_frequency=poll)
    elem = wait.until(expected_conditions.visibility_of_element_located(locator=locator))
    return elem

 

显性等待如果还是找不到元素怎么办???----报错:TimeoutException

 

面试题:等待方式的选择

1.优先:隐性等待:全局设置,等待查找元素

2.再:显性等待:等待元素可以被点击,可见(如果隐性的等待可以找到元素,就不需要再进行显性等待)

3.最后:强制等待:上面2个等待都用不了,就用强制等待;用在多个系统交互的地方,就要用强制等待。

 

总结:

1.元素等待

首先用find_element()进行查找,看看是不是要进行元素等待。(有些元素不需要动态加载的,是不用等待的),如果找不到,用隐性等待。隐性等待找不到,换用现性等待;

现性等待找不到的话,要记得切换条件(presence、visible、clickable);还是找不到再用强制等待。

2.元素定位不到的原因有哪些?nosuchelement

①检查 元素定位的方式是否正确

②没有加元素等待  (调试的时候直接用强制等待,效果比较明显。’具体的业务逻辑按照上面的方式)

③检查 有没有在这个页面上:有没有在这个窗口上;是不是在iframe中

 

二、三大切换 --71节41min

 为什么需要切换窗口?---因为selenium不会自动进行页面的跳转动作

举例:如下在百度页面定位的超链接文本lemon.ke.qq.com/,点击进入该页面,打印出来的current_url是百度页面的url,不是跳转后的url

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

#初始化浏览器对象
driver = webdriver.Chrome()

#打开百度
driver.get("http://www.baidu.com")

#定位百度输入框input
elem = driver.find_element_by_id("kw")
elem.send_keys("柠檬班")

#定位“百度一下”
elem=driver.find_element_by_id("su")
elem.click()

####定位到查链接文本:图片
#先进行等待--这里用显性等待
#设置定时器wait
wait = WebDriverWait(driver,timeout=5,poll_frequency=0.5)

#设置满足的条件
locator= ('xpath','//*[text()="lemon.ke.qq.com/"]')
elem = wait.until(expected_conditions.element_to_be_clickable(locator=locator))
elem.click()

print(driver.current_url)

结果如下:(不是lemon.ke.qq.com)

 

 

页面切换后,才能进行新页面的元素定位。

1.窗口切换

1)窗口切换的函数:driver.switch_to.window(参数) 

          参数就是窗口句柄:window_handle

首先获取所有的窗口句柄:driver.window_handles

 web自动化测试中,常用的就是切换到最新跳转的页面,即driver.window_handles[-1]

 代码:

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

#初始化浏览器对象
driver = webdriver.Chrome()

#打开百度
driver.get("http://www.baidu.com")

#定位百度输入框input
elem = driver.find_element_by_id("kw")
elem.send_keys("柠檬班")

#定位“百度一下”
elem=driver.find_element_by_id("su")
elem.click()

####定位到查链接文本:图片
#先进行等待--这里用显性等待
#设置定时器wait
wait = WebDriverWait(driver,timeout=5,poll_frequency=0.5)

#设置满足的条件
locator= ('xpath','//*[text()="lemon.ke.qq.com/"]')
elem = wait.until(expected_conditions.element_to_be_clickable(locator=locator))
elem.click()

print(driver.current_url)

#窗口切换
  #获取所有的窗口句柄
print(driver.window_handles)
driver.switch_to.window(driver.window_handles[-1])
print(driver.current_url)

结果:

 

2.iframe切换  ---71节50min

 1)如何确认当前页面中有没有iframe?

   -------F12,看当前页面下面的bar,是否有2个html

 

 

 如果页面中有iframe,定位元素是定位不到的。必须进行iframe切换。

 语法:driver.switch_to.frame()

 首先看下frame()的源码:

 

 

2). iframe切换有3中方式:

1.通过iframe的name属性进行定位切换:driver.switch_to.frame('frame_name') <-----------提供name属性的情况下

2.通过iframe的索引进行切换:driver.switch_to.frame(1)---0表示第一个,1表示第二个 (这个方法基本不用)

3.通过查询所有iframe元素,再进行索引(返回的是个webelement对象):driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0]) <-------没有name属性的情况下

 实操:

首先:自己写个带有iframe的页面,如下,iframe是百度首页

 

 

 

 要定位iframe中的百度input输入框,就要进行iframe切换。代码如下:

from selenium import webdriver

#初始化driver浏览器对象
driver = webdriver.Chrome()

#访问有iframe的页面
driver.get("http://localhost:63342/study/lesson_32/iframe_why.html?_ijt=tpfqjvn2hv7su44vb95lke4q0l") 

#带有iframe,进行定位
##首先找到要切换的iframe ----通过xpath找到iframe
frame_elem = driver.find_element_by_xpath('//iframe[@src]')
##进行iframe切换
driver.switch_to.frame(frame_elem)
#定位返回的事webelement对象
elem=driver.find_element_by_id("kw")
print(elem)

结果:

注:上面的get中的url,每次要重新将写的HTML网页重新打开,用最新的url地址。

 

 

72节

3)要定位主页面的元素-------------------已经在iframe中,怎么定位iframe外的页面的元素?

语法:driver.switch_to.default_content()

带有iframe网页的HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>title</title>
</head>
<body>
    <div>
        <ol>
            <li>胡歌</li>
            <li>彭于晏</li>
        </ol>
    </div>

    <iframe src="http://www.baidu.com"></iframe><br>

    <div>
        <p id="hello">python</p>
    </div>
</body>
</html>

 

from selenium import webdriver

#初始化driver浏览器对象
driver = webdriver.Chrome()

#访问有iframe的页面
driver.get("http://localhost:63342/study/lesson_32/iframe_why.html?_ijt=fkgd5gchk67oah046kkqr8vats")

#带有iframe,进行定位
##首先找到要切换的iframe ----通过xpath找到iframe
frame_elem = driver.find_element_by_xpath('//iframe[@src]')
##进行iframe切换
driver.switch_to.frame(frame_elem)
#定位返回的事webelement对象
elem=driver.find_element_by_id("kw")
print(elem)

#定位主页面的元素
##先切换回主页面
driver.switch_to.default_content()
elem = driver.find_element_by_id("hello")
print(elem)

 

4)如果主页面中有一个iframe,iframe中还套有iframe,怎么办?

 先通过xpath找第一次的iframe,再通过xpath找第二层iframe,以此类推。。。。。。。。。。。

 

5)在多层iframe中,如何退回到主页面?

通过parent_frame()一层一层退出来。

语法:switch_to.parent_frame()

 

※特殊情况:

iframe等待:不能用隐性等待(因为隐性等待只能等元素,等待了iframe的元素,但是不能等待iframe这个页面)

所以iframe基本上都是用显性等待。

代码实现:

frame_to_be_available_and_switch_to_it:直到等待有效frame,再切换。可以实现2步操作的类(在第一个轮循时间内没有等待到,再进入下一个轮循时间)

等待不到,报NoSuchFrameException

from selenium import webdriver

#初始化driver浏览器对象
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

driver = webdriver.Chrome()

#访问有iframe的页面
driver.get("http://localhost:63342/study/lesson_32/iframe_why.html?_ijt=elih8hfpjrq3j99ucucuhu5cod")

##首先找到要切换的iframe ----通过xpath找到iframe
frame_elem = driver.find_element_by_xpath('//iframe[@src]')

#iframe切换结合显性等待 WebDriverWait(driver,timeout=20,poll_frequency=0.5).until( expected_conditions.frame_to_be_available_and_switch_to_it(frame_elem)) #frame中定位id elem=driver.find_element_by_id("kw") print(elem) #定位主页面的元素 ##先切换回主页面 driver.switch_to.default_content() elem = driver.find_element_by_id("hello") print(elem)

附上frame_to_be_available_and_switch_to_it类的源码:

class frame_to_be_available_and_switch_to_it(object):
    """ An expectation for checking whether the given frame is available to
    switch to.  If the frame is available it switches the given driver to the
    specified frame.
    """
    def __init__(self, locator):
        self.frame_locator = locator

    def __call__(self, driver):
        try:
            if isinstance(self.frame_locator, tuple):
                driver.switch_to.frame(_find_element(driver,
                                                     self.frame_locator))
            else:
                driver.switch_to.frame(self.frame_locator)
            return True
        except NoSuchFrameException:
            return False

 

 

3.alert弹框切换

先写个带有alert弹窗的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>alert</title>
</head>
<body>
    <p>selenium python autotest</p>
    <p id="leain" onclick="alertMe()"> learn python</p>

    <script>
        function alertMe(){
            alert("this is a alert window!")
        }
    </script>

</body>
</html>

页面显示如下:

 

 出现弹框alert以后,就无法对主页面进行元素定位(主页面被alert遮盖,无法操作。)

所以在出现弹框后,要对主页面进行元素定位,必须先将alert关闭---》点击alert弹窗的【确定】按钮,关闭弹窗。

(1).语法:

①切换到alert弹框,alert是属性(源码的方法上加了@property)

②alert中的确定按钮,alert_elem.accept()

                 取消按钮:alert_elem.dismiss()

代码实现:

from selenium import webdriver

#初始化driver浏览器对象
driver = webdriver.Chrome()
driver.get("http://localhost:63342/study/%E5%85%83%E7%B4%A0%E7%AD%89%E5%BE%85/alert.html?_ijt=pbds0nqdgd9ighds0bdgdfeo8p")

#id 定位元素learn python,并点击
driver.find_element_by_id("learn").click()
#切换到alert
alert_elem = driver.switch_to.alert
#点击确定
alert_elem.accept()

 

(2)alert等待的条件----alert_is_present()

alert_is_present():不需要传参数

源码如下,__init__(self)没有参数需要传递,直接调用call方法

class alert_is_present(object):
    """ Expect an alert to be present."""
    def __init__(self):
        pass

    def __call__(self, driver):
        try:
            alert = driver.switch_to.alert
            return alert
        except NoAlertPresentException:
            return False

现性等待后切换到alert页面,代码如下

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions

#初始化driver浏览器对象
driver = webdriver.Chrome()
driver.get("http://localhost:63342/study/%E5%85%83%E7%B4%A0%E7%AD%89%E5%BE%85/alert.html?_ijt=t00ugtfhr0mvvdj7017ec14i66")

#id 定位元素learn python,并点击
driver.find_element_by_id("learn").click()

# #切换到alert
# alert_elem = driver.switch_to.alert

#显性等待alert并切换
alert_elem = WebDriverWait(driver,timeout=20,poll_frequency=0.5).until(expected_conditions.alert_is_present())

#点击确定
alert_elem.accept()

 总结:

1.窗口切换,iframe切换需要加参数,alert切换不需要加参数,的原因?

一个页面只可能有一个alert,但是iframe可能存在多个。