Selenium Web自动化的一些注意点

浏览器copy selector

我们选择元素有一个方法可以快速生成,右键点击元素点击检查定位到元素后,右键点击元素有个Copy,选择Copy selector

image.png
也会把选择的表达式选择下来Copy selectorCss的写法,我们看到也有XPath大家也可以试一下,这种方法其实是非常有限的,这种方式是以一个有id的元素为瞄点往下找,如果有id的元素不是本身而是上层节点他就要依赖上层节点和他的相对关系来找,如果上层节点跟他隔得很远,他也会一层层的找打他有id的上层节点,路径会很长如果他中间的任何一个环节发生变化就容易出问题健壮性不好,他不会根据别的方法比如class,这种方法我们了解一下就好很鸡肋。


异常捕获,确保chrom进程退出

我们做练习的时候不可能一次就把作业做出来,总归要做一些调试出错调试出错,肯定遇到过出错浏览器就没法关闭的状况,有的同学会想不关就不关呗我们手动关上就好了,调试的时候是可以的,假如我们做系统自动化的时候有很多的用例上百个web自动化用例在执行,如果遇到报错浏览器没有关闭,第二天我们去看的话机器就像卡死了一样,因为很多用例遇到报错比如找不到元素的错误导致浏览器开了二三十个在那甚至更多,我们可以把自动化逻辑放在异常捕获try里面`,看下面例子

from selenium import webdriver
import time

driver = webdriver.Chrome('E:\ChromDriver\chromdriver2.43\chromedriver.exe')
driver.implicitly_wait(10)

driver.get('https://www.baidu.com/')
print(driver.title)

driver.find_element_by_id('kk').send_keys('杭州')

driver.quit()

百度的输入框idkw我这里改为kk这时候,由于不能执行到driver.quit,浏览器就不能自动关闭,解决方法

import traceback

from selenium import webdriver
import time

driver = webdriver.Chrome('E:\ChromDriver\chromdriver2.43\chromedriver.exe')
driver.implicitly_wait(10)
try:
    driver.get('https://www.baidu.com/')
    print(driver.title)

    driver.find_element_by_id('kk').send_keys('杭州')

except:
    print(traceback.format_exc())
finally:
    driver.quit()0

写在try里面,finally 后面需要driver.quit()


我们操作界面的时候不需要点击他,把鼠标移动到上面就会导致页面的变化,比如我们打开华为官网https://www.vmall.com/

image.png
鼠标放在上面元素就会弹出出来,大家可能发现我们这里把鼠标放在上面直接右键查看元素也是可以定位到元素的,其实他本身也在HTML当中即使光标不放在上面,我们能不能不模拟光标放在上面,可以找到直接去选择元素(笔记本电脑之类的)吗?像这些界面元素如果没有把光标停在上面让界面显示出来,通常是不能操作他的会出错,因为当前这个元素如果处于没有显示出来的状态你就想试着去操作他,其实这个元素有个属性
image.png
class等于none的这种属性他的displaynone
image.png
display元素是none的意思是他当前在界面放在HTML里面但是他不显示出来不显示在界面上,Selenium是高度模拟用户点击操作的,因为他没有显示出来这个时候你点击他会出错的,我们可以把它冻结
image.png
这个时候就不会消失了,这个时候可以去查看内容了


Selenium怎么模拟这个移动鼠标到某个元素的动作呢?通过ActionChains 类,ActionChains类 里面提供了 一些特殊的动作, 比如移动鼠标到某个元素,就是其中之一,ActionChains本身有个ActionChains模块文件本身实现了一个ActionChains类,我们查看这个类的放方法

image.png
可以看到他的方法有
image.png
click点击,click_and_hold点击按住不动不要松,context_click右键点击,double_click双击等这里不一一介绍大家自己去研究,我们这里用到的就是move_to_element悬停就使用他

from selenium import webdriver
import time

executable_path = r"d:\tools\webdrivers\chromedriver.exe"
driver = webdriver.Chrome(executable_path)

driver.get('https://www.vmall.com/')

# ---------------------------------------
# 导入ActionChains这个类
from selenium.webdriver.common.action_chains import ActionChains
# 创建实例对象
ac = ActionChains(driver)
#移动不点击  .perform()执行
ac.move_to_element(driver.find_element_by_id('zxnav_1')).perform()

ele= driver.find_element_by_css_selector(
    '#zxnav_1 > div.category-panels.relative > ul > li:nth-child(1) > a')

ele.click()
# ---------------------------------------
input('...')
driver.quit()


下面我们看一段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里面有个divdiv里面有个span里面有个input输入框,里面有个很奇怪的属性margin-top
他的意思是

image.png
这个元素和他的上面元素的间隔有多高2200px像素,这个间隔是很高的,大家复制这段html看一下,比如做自动化的时候web页面很长,他的内容在很下面,我们需不要把这个页面滚动到下方让他出来再去操作这个元素呢?大家注意华为官网的看不到是因为元素所在的区域已经有了,只是你没有操作他,它属于不可见属性的看不到,但是我们现在讲的这种其实可见状态只是你滚动条没有拉下来,通长这种情况不需要你滚动下来的,它本身在视图上是有的只是没呈现滚动区域里面

from selenium import webdriver
executable_path = r"d:\tools\webdrivers\chromedriver.exe"

driver = webdriver.Chrome(executable_path)
driver.implicitly_wait(10)


driver.get('file:///D:/gsync/workspace/sq/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'])

下面是对网易云音乐写的代码,可拿去试一下

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()

假如还一种情况要找的元素他在特别的右边或特别的下边窗口最大化都显现不出来,那就需要滚动元素的方案,selenium没有直接的方法,可以使用最后一招, 直接让浏览器执行javascript脚本浏览器执行的语言是js, 如果我们能直接传入js代码让浏览器执行,就得到了最大的灵活性去控制浏览器。可以算是终极武器。 比如滚动屏幕,就可以使用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)')

上滚加个负号就好了


界面重新绘制

接下来说一下界面里面元素出现动态改变的这个情况,我们看一个例子,下面截图是一个教管系统!

image.png

假如我们要实现一个自动化,要把所有的课程信息删除掉,如果按照之前的方法我们点击“删除”然后点击“确定”,把所有的删除按钮都找到,看似很简单我们按照这个思路去写代码,根据我们的思路要把所有的删除按钮找出来
image.png
大家看一下根据什么属性去找呢,没有id根据class,但是这个class有好多属性,btn-green看名字就可以看出来按钮是绿色的btn-outlined是外面有一个方框,根据这个class是不好去找的因为编辑也有同样的属性,这里根据ng-click这个属性去找比较好,他这个属性对应删除的动作,点了这个删除之后会执行一段JavaScript代码或函数,删除是跟这个紧密相关的,点击删除之后这里还有个确定,
image.png
这个元素在这里很好找,我们就不讲解了

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)

我们分析一下报错WebDriver报的Exception,unknown error不知道什么类型,我们看下error的报错Element <button >对应的是这个webelement对象,看后面的就是我们删除按钮web的写法<button type="button" class="btn btn-green btn-outlined btn-xs ng-scope" ng-click="delOne(one)",报is not clickable at point是不可点击的,Other element would receive the click另外一个元素接收到点击的动作,这个元素为什么不可点击呢。大家想一下刚才的运行结果,确实已经删除了一个元素了到第二个删除操作的时候就报错了,大家以后做自动化的时候会经常遇到这种事情,因为现在web前端的开发很多开发人员使用的方法都会动态的更新的前端界面,之前最早的web界面的呈现,比如导致web界面出现了变化,都是发起http请求,后端返回一个完整的http页面给你,以前都是这样,随着前端开发衍进觉得这种效率太低,因为有的时候并不需要后端重新产生一个完整的界面,比如这个删除操作,删除一个课程只需要告诉后端我把这个课程删了,服务器处理一下在数据库把这个课程清除掉,没有必要让服务端再返回一个完整html的界面给前端,对于我们来说只需要在代码里动态的把课程清除掉就可以,所以这个时候前端的开发可以动态改变前端的内容,可以通过JavaScript代码更新界面,或者一些框架,这样就产生了一个问题就像之前这样看似点击删除按钮,点击确定好像界面只少了一个课程,但实际上有的框架就不是简单的把这一个元素删了,他有可能把整个表格获取一次重新产生了,重新产生意思是原来是4条记录现在重新产生了眼睛看起来就少了一条ji记录,实际上页面上所有的元素都重新创建了一遍,这就带来我们删除一门课程之后导致现在界面剩下来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)就会立即获取页面,就会获取到没有更新的页面元素


看一个练习:
Selenium 作业 6
作业1

登录华为官网 https://www.vmall.com/
点击 "华为官网" 链接

查 "华为官网" 页面上是否 有如下主菜单

智能手机
笔记本
平板
穿戴设备
智能家居
更多产品
软件应用
服务与支持

最后再回到主窗口, 检查鼠标停留在 "笔记本&平板" 处的时候, 是否显示的菜单有
"平板电脑 笔记本电脑 笔记本配件"

怎么模拟鼠标停留事件,请大家自行网上搜索,看看能不能自己解决问题。

from selenium import  webdriver
import time

from selenium.webdriver import ActionChains

driver = webdriver.Chrome('E:\ChromDriver\chromdriver2.43\chromedriver.exe')
driver.get('https://www.vmall.com/')
driver.implicitly_wait(10)
# print(driver.title)
#主窗口handle
main_window=driver.current_window_handle
#点击进入华为官网
driver.find_element_by_css_selector('a[href="http://consumer.huawei.com/cn/"]').click()

for handle in driver.window_handles:
    driver.switch_to.window(handle)
    if '华为消费者业务官网' in driver.title:
        # print(handle)
        break

#找到主菜单
orders_eles = driver.find_elements_by_css_selector('ul.clearfix.nav-cnt a.a-common.products-name')

orders_txt = [ele.text for ele in orders_eles]
expect_orders='''智能手机
    笔记本
    平板
    穿戴设备
    智能家居
    更多产品
    软件应用
    服务与支持
'''
expect_text = [order.strip() for order in expect_orders.split('\n')]
if orders_txt == expect_text:
    print('匹配菜单成功')
else:
    print('匹配失败')
    # print(orders_txt)
    # print(expect_text)
#回到主页面
driver.switch_to.window(main_window)
# print(driver.title)

#鼠标停留在笔记本菜单
ele = driver.find_element_by_id('zxnav_1')
ActionChains(driver).move_to_element(ele).perform()

#检查菜单项
css='li#zxnav_1 div.category-panels.category-panels-1 span'
orders = driver.find_elements_by_css_selector(css)
order_text = [order.text for order in orders]
# print(order_txt)
expect_txt = u'''平板电脑|笔记本电脑|笔记本配件'''
order_txt='|'.join(order_text)

if expect_txt == order_txt:
    print('匹配菜单成功')
else:
    print('匹配失败')
    print(expect_txt)
    print(order_txt)


driver.quit()

作业2
写一个程序实现如下的自动化过程

  • 登录 https://tinypng.com/
  • 点击 上传文件的虚线框
  • 选择 插图,在本地目录中选择一张准备好的图片 , 查看是否能够上传图片成功
from selenium import webdriver

import time

driver = webdriver.Chrome('E:\ChromDriver\chromdriver2.43\chromedriver.exe')
driver.implicitly_wait(10)


driver.get('https://tinypng.com/')
print(driver.title)
#点击上传图标
driver.find_element_by_css_selector("figure.icon").click()

# 直接发送键盘消息给 当前应用程序,
# 前提是浏览器必须是当前应用
#   pip install pypiwin32
import win32com.client

shell = win32com.client.Dispatch("WScript.Shell")

# 有的系统要加 '\r'
# 有的系统要加 '\r\n'
img_name='1.jpg'
shell.Sendkeys('e:\\' + img_name + '\n'+ '\n')#在当前的焦点应用程序发送一个字符串
time.sleep(3)

file_ele = driver.find_element_by_css_selector('div.before:nth-of-type(1)')

print(file_ele.text)
file_name = file_ele.text.split('\n')[1]

if img_name.upper() == file_name.upper():
    print('上传成功')
else:
    print('上传失败')
input('...')
driver.quit()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容