3.9 窗口控制
下面我们看一段 HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>定位网页元素</title>
</head>
<body style="width:1600px">
<div>
<span>输入框</span> <input type="text" style="margin-top:2200px">
</div>
</body>
</html>
可以看到这段 HTML 很简单,body 里面有个 div,div 里面有个 span 里面有个 input 输入框,里面有个很奇特的属性 margin-top 它的意思是这个元素和他的上面元素的间隔有多高 2200px 像素,这个间隔是非常高的,
大家复制这段 html 看一下,比如做自动化的时候 web 页面很长,它的内容在很下面,我们需不要把这个页面滚动到下方让他出来再去操作这个元素呢?大家注意华为官网的看不到是因为元素所在的区域已经有了,只是你没有操作它,它属于不可见属性的看不到,但是我们现在讲的这种其实可见状态只是你滚动条没有拉下来,通长这种情况不需要你滚动下来的,它本身在视图上是有的,只是没呈现滚动区域里面。
from selenium import webdriver
executable_path = r"d:\tools\webdrivers\chromedriver.exe"
driver = webdriver.Chrome('E:\ChromDriver\chromedriver.exe')
driver.implicitly_wait(10)
driver.get(r'E:\case\Selenium\代码\samples_selenium\wd\lesson07\winsize.html')
searchbox = driver.find_element_by_tag_name('input')
searchbox.send_keys('你好啊\n')
input('...')
driver.quit()
如果确实需要改变窗口大小,有时确实让元素可见,才能点击(有时候确实是这样,不知道什么原因)或者觉得这样看起来更清楚一些。
- 获取当前窗口的大小返回是字典格式
size = driver.get_window_size() #返回是字典格式
- 调整窗口的大小,变宽、变高、变矮
driver.set_window_size(1100,size['height']) #改变宽度
set_window_size(1100,size['height'])
参数是多少个像素(我们这里是1100)。我们通常自动化的时候大家发现窗口可能并没有最大化 ,如果我们想把它调宽就 set_window_size(1100,size['height']) 第一个参数就是它的宽度。第二个参数就是它的高度,如果你觉得它不够高,可以改变它的高度。
driver.set_window_size(size['width'],500) #改变高度
- 最大化
driver.maximize_window()
下面是对网易云音乐写的代码,可拿去试一下。
from selenium import webdriver
driver = webdriver.Chrome(r"E:\ChromDriver\chromedriver.exe")
driver.implicitly_wait(10)
driver.get('http://music.163.com')
# 结果像这样 {'width': 855, 'height': 922}
size = driver.get_window_size()
print(size)
# 只改变宽度
driver.set_window_size(1300, size['height'])
#最大化
driver.maximize_window()
searchbox = driver.find_element_by_css_selector('#srch')
searchbox.send_keys('张学友\n')
driver.quit()
小知识点:
在 Windows 文本文件里面用 \r\n 表示回车加换行,但实际上 \n 是 newline可以表示下一行,通常在输入里面代表下一行 \n 就相当于敲了一个回车键,相当于换一行的意思。
上面我们讲了改变窗口大小的方法,但是有些时候我们把窗口的高度和宽度最大化元素还是看不到,因为要找的元素它在特别的右边或特别的下边窗口最大化都显现不出来,那就需要滚动元素的方案,selenium 没有直接的方法,可以使用最后一招, 直接让浏览器执行一段 javascript 脚本,因为浏览器它执行的前端代码用 JavaScript 写的,我们做 selenium 自动化,我们自动化程序把命令发送给浏览器驱动,浏览器驱动发送给浏览器,其实我们可以直接把它们要执行的代码发给它,就得到了最大的灵活性去控制浏览器。可以算是终极武器。 比如滚动屏幕,就可以使用 js 语言。
driver.execute_script('window.scrollBy(250,0)')
第一个参数250是横向滚动就是x坐标滚动,向右滚250个像素,如果有们想向左滚
driver.execute_script('window.scrollBy(-250,0)')就滚回去了
第二个参数是向下滚
driver.execute_script('window.scrollBy(0,300)')
上滚加个负号就好了。
下面是对网易云音乐写的代码,可以试一下效果:
from selenium import webdriver
driver = webdriver.Chrome('E:\ChromDriver\chromedriver.exe')
driver.implicitly_wait(10)
driver.get('http://music.163.com')
driver.execute_script('window.scrollBy(200,0)')
searchbox = driver.find_element_by_css_selector('#g_search input')
searchbox.send_keys('张学友\n')
driver.quit()
界面重新绘制
有这样一个教管系统,有课程管理、老师管理、培训班管理、培训班期管理、课时管理、学生管理,其中课程管理里面可以删除、添加、修改课程,假如我们要做这样一个自动化功能,做自动化之前比如有个用例自动化之前,需要当前系统中一门课程都没有。
就有一个初始化的操作,就是把当前系统中如果有课程全部删掉,全部删掉我们手工怎么做,手工删掉就是 点击“删除” 然后点击“确定”。我们自动化无非也是做这个操作,把所有的删除都点一遍。看似很简单我们按照这个思路去写代码,根据我们的思路要把所有的删除按钮找出来。
大家看一下根据什么属性去找呢,没有 id 根据 class,但是这个 class 有好多属性,btn-green 看名字就可以看出来按钮是绿色的按钮 btn-outlined 是外面有一个方框,根据这个 class 是不好去找的因为编辑也有同样的属性,这里根据 ng-click 这个属性去找比较好,他这个属性对应删除的动作,点了这个删除之后会执行一段 JavaScript 代码或函数,删除是跟这个紧密相关的,点击删除之后这里还有个确定。
这个元素在这里很好找,我们就不说了。
代码:
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('http://localhost/mgr/login/login.html')
#登录
driver.find_element_by_id('username').send_keys('auto')
driver.find_element_by_id('password').send_keys('sdfsdfsdf')
driver.find_element_by_tag_name('button').click()
time.sleep(1)
#根据ng-click去找删除按钮,这里是个list
delButtons = driver.find_elements_by_css_selector(
'*[ng-click^=delOne]')
for button in delButtons:
button.click()
time.sleep(1)
driver.find_element_by_css_selector(
'.modal-footer .btn-primary').click()
运行会发现删除第一个之后就不动了,报错了报错信息如下:
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
(Session info: chrome=74.0.3729.157)
(Driver info: chromedriver=74.0.3729.6 (255758eccf3d244491b8a1317aa76e1ce10d57e9-refs/branch-heads/3729@{#29}),platform=Windows NT 10.0.17134 x86_64)
问题出在 button.click() 这一行,为什么点击会有异常呢,我们看一下描述stale element reference:
,stale 过期的意思,这个元素已经过期了,就是过期元素的引用,什么元素过期呢,我们大胆的猜一下就是这个 button 对应的这个元素。element is not attached to the page document
就是 button这个元素已经不在当前页面这个文档里了。大家以后做自动化的时候会经常遇到这种事情,因为现在 web 前端的开发很多开发人员使用的方法都会动态的更新的前端界面,之前最早的 web 界面的呈现,比如导致 web 界面出现了变化,都是发起 http 请求,后端返回一个完整的 http 页面给你,以前都是这样,随着前端开发衍进觉得这种效率太低,因为有的时候并不需要后端重新产生一个完整的界面,比如这个删除操作,删除一个课程只需要告诉后端我把这个课程删了,服务器处理一下在数据库把这个课程清除掉,没有必要让服务端再返回一个完整html的界面给前端,对于我们来说只需要在代码里动态的把课程清除掉就可以,所以这个时候前端的开发可以动态改变前端的内容,可以通过 JavaScript 代码更新界面,或者一些框架,这样就产生了一个问题就像之前这样看似点击删除按钮,点击确定好像界面只少了一个课程,但实际上有的框架就不是简单的把这一个元素删了,他有可能把整个表格获取一次重新产生了,重新产生意思是原来是4条记录现在重新产生了眼睛看起来就少了一条记录,实际上页面上所有的元素都重新创建了一遍,这就带来我们删除一门课程之后导致现在界面剩下来 3 个删除按钮已经不是刚才 4 个中的 3 个了,就是这个原因导致的。我们可以这样既然你之后界面刷新了那我们每次删除之后就重新获取当前界面的元素就可以了。
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('http://localhost/mgr/login/login.html')
driver.find_element_by_id('username').send_keys('auto')
driver.find_element_by_id('password').send_keys('sdfsdfsdf')
driver.find_element_by_tag_name('button').click()
time.sleep(1)
driver.implicitly_wait(1)
while True:
#找删除按钮
delButtons = driver.find_elements_by_css_selector(
'*[ng-click^=delOne]')
#判断有没有删除按钮
if delButtons == []:
break
#如果有就找到第一个元素,点击他
delButtons[0].click()
#点确定
driver.find_element_by_css_selector('.modal-footer .btn-primary').click()
time.sleep(1)
driver.implicitly_wait(5)
这样就没有问题,因为我们每次删除按钮都会重新获取当前页面,大家会不会有个疑惑循环前有个 implicitly_wait(1) 循环后又把 implicitly_wait(1) 改成implicitly_wait(5) 呢,主要是因为循环删除总有一次删光了,删光了最后一次循环执行 delButtons = driver.find_elements_by_css_selector('[ng-click^=delOne]')* 的时候,因为删光了就没有元素,就会等待全局的等待时间比如5秒,就会等待5秒,临时的把时间改短了点这样比较好。这里的time.sleep(1)很重要,因为你点击确定删除的时候就会执行删除操作,服务端就会删除数据,界面刷新会花一段时间,如果没有sleep(1)就会立即获取页面,就会获取到没有更新的页面元素。
3.9.1 用半自动化的方法
有些自动化的输入需要人来输入的,比如我们常见的 验证码,我们可以打开12306 的登录看一下:
点击图中的所有的网球拍,你自动化怎么做,非常难做,人都很难看清楚何况用软件,即使做了失败率也是很高的,还有的时候检查很难,比如我们打开华为商城,比如说测试用例里面有一步检查当前界面的布局有没有错乱。那怎么检查,怎么用软件检查页面有没有错乱或者检查图片 log 是否正确,不好做。。。人工智能还没有到这一步,这些动作还是得人来做。这时候有人会说,假如我测试用例有这样的步骤,是不是就要跳掉了,不做自动化了。这个时候我们通常会采取半自动化的方式,因为只是其中某一个步骤需要人干预一下。半自动化在工作中也是非常常用的。我们可以在需要人干预的时候可以做出一些提示,比如说发出一些声音来。
import winsound
winsound.Beep(1500,3000)
这是我们随便举的一种方法,方法有很多。