selenium day01、02
python 3.6.6
selenium 3.141.0
chrome 99.0.4844.51
chromedriver 99.0.4844.51
Firefox 98.0.1
geckodriver v0.30.0
自动化脚本编写原则:LOVE原则
L:Locate,定位
O:Operate,操作
V:Verify,断言
E:Except,异常处理
-------------------------------------
1. 环境准备
1-1. 安装selenium
pip install selenium
pip install 包名 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
安装成功后,..\Python36\Lib\site-packages里面可看到selenium相关文件,
执行 pip show selenium 可查看版本,
使用 from selenium import webdriver 导包时不会报错
1-2. 安装浏览器及驱动
不同的浏览器,其内核不同
Chrome离线下载:https://www.google.cn/chrome/?standalone=1&platform=win64
Chrome、Firefox驱动下载:https://npm.taobao.org/mirrors/
(Firefox驱动是geckodriver)
Firefox下载:https://www.firefox.com.cn/download/
火狐版本及驱动映射:https://github.com/mozilla/geckodriver/releases/
将下载好的压缩包解压后chromedriver.exe移动到..\Python36\Scripts文件夹下
2. 前端页面
打开百度首页,打开F12,使用元素拾取器,标记输入框,可看到元素及CSS样式
在html标签中,如li表示列表,input表示输入框或按钮,a表示超链接
3. 打开网页
# 1.导入工具包
from selenium import webdriver
# 2.打开浏览器,注意第一个字母大写
dr = webdriver.Chrome() # 获取浏览器句柄
# dr = webdriver.Firefox()
# 以下是360浏览器
# options = webdriver.ChromeOptions()
# options.binary_location = r'D:\Program Files1\360se\360se6\Application\360se.exe'
# dr = webdriver.Chrome(r'D:\chromedriver.exe', options=options) # 对应驱动的路径
# 3.打开被测页面
url = "https://www.baidu.com"
dr.get(url)
4.八大元素定位方法 (使用条件是元素及对应属性值要唯一)
4-1.根据id属性定位:find_element_by_id(id的值),元素及对应属性值可以查看源码搜索
inputBox = dr.find_element_by_id("kw") # 定位输入框
inputBox.send_keys("selenium自动化") # 模拟键盘输入
sBtn = dr.find_element_by_id("su") # 定位搜索按钮
sBtn.click() # 点击按钮
4-2.根据name属性定位:find_element_by_name(name的值)
4-3.根据class属性定位:find_element_by_class_name(class的值)
当class有两个值时,(如class="placeholder lowie-bg"),
输入其中一个唯一的值即可(placeholder 或 lowie-bg)
4-4.根据超链接文本定位:find_element_by_link_text(超链接文本信息)
4-5.根据超链接部分文本信息定位:find_element_by_partial_link_text(部分文本信息)
4-6.根据标签定位:find_element_by_tag_name(标签名称) , 实际较少使用
#如果有重复属性值,需要定位一组元素:find_elements_by_xxxx ,注意元素的s
#注:尽量不用同一种定位方法,容易报错,原因未知
tags = dr.find_elements_by_tag_name("input")
print(len(tags),type(tags))
for ele in tags:
if ele.get_attribute("id") == "kw": # 通过获取元素属性值定位
ele.send_keys("百度咋回事儿")
sBtn = dr.find_element_by_id('su') # 换了另一种定位方法,避免出错
sBtn.click()
4-7.根据xpath定位:
4-7-1.根据绝对路径定位:find_element_by_xpath(绝对路径),以 / 开头,实际较少使用
find_element_by_xpath('/html/body/div[5]/div/span[2]/input')
# [5]表示第5个位置的标签
4-7-2.根据相对路径定位:find_element_by_xpath(相对路径),以 // 开头
1).根据元素的单个属性定位:find_element_by_xpath('//input[@type="submit"]')
2).根据元素的多个属性定位:find_element_by_xpath('//*[@name="wd" and @class="s_ipt"]')
# 标签如果用*表示,代表所有的标签
3).根据层级+元素属性定位:find_element_by_xpath('//span[1]/input[@id="kw"]')
# 父子级标签均可加属性值[@...]或位置标记[2],也均可同时加属性值和位置标记,顺序不分先后
4).根据层级+层级位置定位:find_element_by_xpath('//span[1]/input')
# 如果层级标签唯一,位置标记也可不加
5).根据元素的文本信息定位:find_element_by_xpath('//span[text()="设置"]')
# 文本信息要输入全部文本,且前面没有@符号
4-8.根据css定位:find_element_by_css_selector('css的语法')
1).根据id的属性定位 ('#id')
# 如id="kw",写成('#kw')
2).根据class的属性定位:('.class') 或 ('.class1.class2')
# 如class="s_ipt",写成('.s_ipt')
# 如class="bg s_btn",括号里面需要写('.bg.s_btn'),中间没有空格
3).根据id和class之外的属性定位:('[属性]')或('标签[属性]')或('[属性1][属性2]')
# ('[name="wd"]') # 不带标签的属性
# ('input[name="wd"]') # 带标签的属性
# ('[name="wd"][id="kw"]') # 多个属性
# ('[name="wd"]#kw') # 多个属性(name和id)
# ('#kw.s_ipt') # 多个属性(id和class)
4).根据层级+属性定位 ('父级 > 子级[属性=值]') 或 ('父级.class > 子级#id')
# ('span > input')
# ('span > input#kw')
# ('span.bg.s_ipt_wr.quickdelete-wrap > input') # span的class属性值有多个
# ('span[name="goods_id"] > input')
5. 元素定位之By类
from selenium.webdriver.common.by import By
find_element_by_id -------> find_element(By.ID,'id的属性值')
find_element_by_name -----> find_element(By.NAME,'name的属性值')
find_element_by_xpath -----> find_element(By.XPATH,'xpath语法')
find_element_by_css_selector--> find_element(By.CSS_SELECTOR,'cs语法')
find_element(By.CLASS_NAME,"s_ipt")
find_element(By.TAG_NAME,'标签名称') # 较少使用
find_element(By.LINK_TEXT,'文本信息')
find_element(By.PARTIAL_LINK_TEXT,'部分文本信息')
6. 浏览器元素的操作
6-1.浏览器的控制
dr.maximize_window() 最大化浏览器窗口
dr.set_window_size(a,b) a、b代表的浏览器的长宽
dr.current_url 获取当前url
dr.back() 后退操作
dr.forward() 前进操作
dr.refresh() 模拟F5刷新
6-2.元素的操作方法
.click() 点击事件
.send_keys() 模拟用户输入内容
.clear() 清空输入框的内容
.text 获取指定元素的文本信息
.is_displayed() 判断元素是否在页面显示(肉眼可见的元素才能操作)
返回True表示可见,False表示不可见
.send_keys() 上传文件(该方法上传文件只针对input标签有效)
.get_attribute('属性名称') 获取元素属性的值
等待: 1) time模块的sleep 强制等待,或固定等待
2) dr.implicitly_wait() 隐式等待,只对dr(webdriver)开头的操作生效
# 注:隐式等待中只要页面加载完成就结束等待,隐式等待超时后没有加载完成,
就会直接报异常,隐式等待默认为0秒
3)WebDriverWait() 显式等待,只对某一页面有效,超时报异常,且需要先导包
from selenium.webdriver.support.wait import WebDriverWait
# 等待10秒,每0.2秒检查一次数据
inputBox = WebDriverWait(dr,10,0.2).until(lambda dr:dr.find_element_by_id("kw"))
from selenium.webdriver.support import expected_conditions as EC
# 显式等待结合expected_conditions使用,visibility_of_element_located()判断元素是否可见
inputBox = WebDriverWait(dr,10,0.2).until(EC.visibility_of_element_located((By.ID,"kw")))
inputBox.send_keys(888)
# 注: 显式等待,WebDriverWait可与expected_conditions结合使用,
详见:https://blog.csdn.net/sinat_41774836/article/details/88965281
总结:
1.强制等待,不建议用这种等待方法,严重影响代码的执行速度
2.隐式等待,程序会一直等待整个页面加载完成,直到超时。
有时候我需要的那个元素早就加载完成了,只是页面上有个别其他元素加载特别慢,
仍要等待页面全部加载完成才能执行下一步
3.显式等待,程序每隔xx秒看一眼,如果条件成立了,则执行下一步,
否则继续等待,直到超过设置的最长时间,然后抛出TimeoutException
6-3. 其他操作方法
1) submit() 提交表单,模拟回车键的操作
2) 鼠标悬停、动作生效:
from selenium.webdriver import ActionChains # 导包
move_to_element() 将鼠标移动到对应的元素
perform() 执行所有ActionChains中存储的行为,即让悬停的动作生效
例:找到百度首页的“更多”,鼠标移过去展开“更多”
from selenium.webdriver import ActionChains # 导包
moreBtn = dr.find_element_by_name('tj_briicon') # 定位首页的“更多”元素
ActionChains(dr).move_to_element(moreBtn).perform() # 移动鼠标到“更多”并展开
3)多表单切换:
dr.switch_to.frame() # 切换表单
dr.switch_to.default_content() # 切回默认表单(最外层表单)
某些页面(如https://mail.163.com/)采用了iframe页面嵌套,即HTML里面嵌套了另一个HTML,
元素定位方法默认定位外层的HTML,如果定位里面的HTML,需要进行表单切换。
例:在网易邮箱的用户名输入内容
email = 'https://mail.163.com/'
dr.get(email)
frame = dr.find_element_by_xpath('//div[@id="loginDiv"]/iframe') # 定位iframe
dr.switch_to.frame(frame) # 切换到表单2
username = dr.find_element_by_name('email')
username.send_keys('iframe定位')
如果表单1里面有并列的表单2和表单3,多表单切换顺序为:
默认表单1 → 表单2 → 默认表单1 → 表单3
切回默认表单:
dr.switch_to.default_content() # 切回默认表单1
dr.switch_to.frame(表单3) # 切换到表单3
4)窗口切换
windows = dr.window_handles # 获取所有打开的窗口
# print(len(windows)) # 打印列表长度
dr.switch_to.window(windows[1]) # 切换到第2个窗口(取列表第2个元素)
5)关闭窗口
dr.close() #关闭当前窗口, 若关闭多个窗口,需要先切换窗口
dr.quit() #关闭所有窗口,退出驱动
day 02-----------------------------------------------------------------------------
6)警告框处理
页面上有些弹框无法定位,在WebDriver 中处理JavaScript 所生成的网页告警信息
可以使用switch_to.alert方法
# dr.switch_to.alert.text #获取警告框文本信息
# dr.switch_to.alert.accept() #点击警告框的确认按钮
# dr.switch_to.alert.dismiss() #点击警告框的取消按钮
例如:百度首页的右上角设置-搜索设置-保存设置后出现的弹框
element = dr.find_element_by_css_selector('#s-usersetting-top') #定位“设置”
ActionChains(dr).move_to_element(element).perform() #鼠标悬停(需要先导包)
time.sleep(0.5)
setting = dr.find_element_by_css_selector('.setpref') #定位“搜索设置”
setting.click()
saveSetting = dr.find_element_by_link_text('保存设置') #定位“保存设置”
saveSetting.click()
text = dr.switch_to.alert.text #获取警告框文本信息
print(text)
time.sleep(0.5)
dr.switch_to.alert.accept() #点击警告框的确认按钮
7)下拉框元素定位
某些页面(如http://sahitest.com/demo/selectTest.htm)需要选择下拉框指定值
from selenium.webdriver.support.select import Select # 先导包
Select(element).select_by_index(下标) 根据索引定位,从0开始
Select(element).select_by_value("value的值") 根据value属性的值定位
Select(element).select_by_visible_text("文本信息") 根据文本信息定位
例如:定位页面最后一个下拉框,分别选择Cell Phone、Fax、Mail
from selenium.webdriver.support.select import Select # 先导包
url = "http://sahitest.com/demo/selectTest.htm"
dr.get(url)
lastList = dr.find_element_by_css_selector("#s1") # 定位最后一个下拉框
time.sleep(2)
Select(lastList).select_by_index(2) # 根据索引定位
time.sleep(2)
Select(lastList).select_by_value("49") # 根据value属性的值定位
time.sleep(2)
Select(lastList).select_by_visible_text("Mail") # 根据文本信息定位
8)键盘事件Keys类
from selenium.webdriver.common.keys import Keys # 先导包
inputBox = dr.find_element_by_css_selector("#kw")
inputBox.send_keys("自动化测试")
time.sleep(2)
inputBox.send_keys(Keys.BACKSPACE) # 模拟键盘 删除键 backspace
# inputBox.send_keys(Keys.BACK_SPACE) # 模拟删除键的另一种写法
time.sleep(2)
inputBox.send_keys(Keys.CONTROL,"a") # 模拟键盘 复制键 Ctrl + A
time.sleep(2)
inputBox.send_keys(Keys.CONTROL,"x") # 模拟键盘 剪切键 Ctrl + X
time.sleep(2)
inputBox.send_keys(Keys.CONTROL,"v") # 模拟键盘 粘贴键 Ctrl + V
time.sleep(2)
inputBox.send_keys(Keys.ENTER) # 模拟键盘 回车键 Enter
6-4. 浏览器元素操作之进阶篇
在某些特殊情况下,浏览器的元素是无法被定位,或无法被操作的,比如浏览器的滚动条,
或元素具有readonly属性,我们需要使用稍微有难度一点的定位方法---调用js脚本进行定位。
js语句末尾最好加上分号(不加也可)
1)滚动条处理
taobao = 'https://www.taobao.com/'
dr.get(taobao)
time.sleep(2)
location1 = 'window.scrollTo(0,700);' # 定位到(0,700)
dr.execute_script(location1) # 执行定位
time.sleep(2)
location2 = "window.scrollTo(0,300);" # 定位到(0,300)
dr.execute_script(location2)
2)js定位(较少使用,不要求掌握)
除了id是定位到的单个element元素对象,其它的都是elements返回的是list对象。
(注意后面的s)
例如:打开百度首页后,进行元素定位
# 1.根据id定位:定位到输入框,并输入内容
inputBox1 = 'document.getElementById("kw").value="juzi01";'
dr.execute_script(inputBox1)
time.sleep(1)
# 2.根据class定位:定位到输入框,并输入内容 (注意返回的是list,选取第1个)
inputBox2 = 'document.getElementsByClassName("s_ipt")[0].value="juzi02"'
dr.execute_script(inputBox2)
time.sleep(1)
# 3.根据name定位:定位到输入框,并输入内容 (注意返回的是list,选取第1个)
inputBox3 = 'document.getElementsByName("wd")[0].value="juzi03";'
dr.execute_script(inputBox3)
time.sleep(1)
# 4.根据标签名称定位(很少使用):定位到输入框,并输入内容 (注意选取list第8个标签)
inputBox4 = 'document.getElementsByTagName("input")[7].value="juzi04";'
dr.execute_script(inputBox4)
time.sleep(1)
# 5.根据css定位:定位到搜索按钮,并点击
sBtn = 'document.querySelectorAll("#su")[0].click();'
dr.execute_script(sBtn)
3) jQuery定位,跟css_selector定位很像,实际工作中应用较少。
注:整个$(...)都可以放到console里面运行,查看length的值不为0,表示定位到了
# 1.根据id定位
inputBox = '$("#kw").val("自动化测试");'
dr.execute_script(inputBox)
# 2.根据class定位
inputBox2 = '$(".s_ipt").val("自动化测试2");'
dr.execute_script(inputBox2)
# 3.根据其他属性定位:使用的是css语法
inputBox3 = '$("input[name=wd]").val("自动化测试3");'
dr.execute_script(inputBox3)
# 4.根据层次+属性定位
inputBox4 = '$("span > input[id=kw]").val("自动化测试4");'
# inputBox4 = '$("span > #kw").val("自动化测试44");'
dr.execute_script(inputBox4)
# 注:如果input标签有个属性是type="text",可以写成$("span > input:text")
# 5.根据标签索引定位: 标签名:eq(index) ,index指的是索引位
inputBox5 = '$("span > input:eq(0)").val("自动化测试5");' # 找到第1个input标签
dr.execute_script(inputBox5)
# 6.根据标签顺序定位: 标签名:nth-child(n) ,n指的是同一层级下的第n个元素
# inputBox6 = '$("span > input:nth-child(2)").val("自动化测试6");' # 找到第2个元素
# inputBox6 = '$(".bg > input:nth-child(2)").val("自动化测试66");' # 找到第2个元素
inputBox6 = '$("span.bg > input:nth-child(2)").val("自动化测试666");' # 找到第2个元素
dr.execute_script(inputBox6)
# 7.根据标签名和顺序定位: 标签名p:nth-of-type(n),n指的是同一层级下的标签名为p的第n个元素
# inputBox7 = '$("span.bg>input#kw:nth-of-type(1)").val("自动化测试7");' # 找到第1个input
inputBox7 = '$("span.bg.s_ipt_wr>input:nth-of-type(1)").val("自动化测试7");' # 找到第1个input
dr.execute_script(inputBox7)
# 8.根据指定层级的最后一个标签定位: 标签名p:last,在指定层级定位最后一个标签p
inputBox8 = '$(".bg.s_ipt_wr > input:last").val("自动化测试8")'
dr.execute_script(inputBox8)
4) 对元素属性增删改操作
如百度首页的图片上传图标,标签为 <span class="soutu-btn"></span>,修改静态页面的图标不显示,
只需要对标签加个style属性即可,即<span class="soutu-btn" style="display:none;"></span>,
其中,style="display:none;"表示隐藏该元素,style="display:block;"表示显示该元素。
# 例如操作百度首页的图片上传图标,根据js定位的css语法定位到图标,
先增加元素属性,然后修改,最后再移除属性
# 1.在静态页面中,添加元素属性并设为隐藏(style="display:none;)
time.sleep(2)
soutu = 'document.querySelectorAll(".soutu-btn")[0].setAttribute("style","display:none");'
dr.execute_script(soutu)
# 2.在静态页面中,修改元素属性为显示(style="display:block;)
time.sleep(2)
soutu2 = 'document.querySelectorAll(".soutu-btn")[0].style="display:block";'
dr.execute_script(soutu2)
# 3.在静态页面中,修改元素属性为隐藏(style="display:none;)
time.sleep(2)
soutu3 = 'document.querySelectorAll(".soutu-btn")[0].style="display:none";'
dr.execute_script(soutu3)
# 4.在静态页面中,移除元素属性style
time.sleep(2)
soutu4 = 'document.querySelectorAll(".soutu-btn")[0].removeAttribute("style");'
dr.execute_script(soutu4)
5) 删除readonly属性
若元素包含readonly属性,无法直接操作,需要调用JS脚本把readonly属性删掉。
例如某电商平台新增活动时,活动开始、结束时间需要把readonly属性删除
import time
from datetime import date, timedelta #导包一般放在前面
ecshop = 'http://192.168.0.134/ecshop/admin'
dr.get(ecshop)
username = dr.find_element_by_xpath('//input[@name="username"]')
username.send_keys('admin')
password = dr.find_element_by_xpath('//input[@name="password"]')
password.send_keys('admin123')
btn = dr.find_element_by_xpath('//input[@type="submit"]')
btn.click()
activity = dr.find_element_by_css_selector('#menu-frame') #定位到iframe表单A
dr.switch_to.frame(activity) #切换到表单A
time.sleep(1)
duoBao = dr.find_element_by_link_text('夺宝奇兵')
time.sleep(1)
duoBao.click()
dr.switch_to.default_content() #切回默认表单
activity = dr.find_element_by_css_selector('#main-frame') #定位到iframe表单B
dr.switch_to.frame(activity) #切换到表单B
duoBaoAdd = dr.find_element_by_xpath('//span[@class="action-span"]/a')
time.sleep(1)
duoBaoAdd.click()
activityName = dr.find_element_by_xpath('//input[@name="snatch_name"]')
activityName.send_keys('这是夺宝活动名称啊') #填写活动名称
goodsKeys = dr.find_element_by_css_selector('input[value=" 搜索 "]')
goodsKeys.click()
activityDesc = dr.find_element_by_css_selector('textarea[name="desc"]')
activityDesc.send_keys('这是活动描述啊') #填写活动描述
# 移除活动开始时间的readonly属性
js = '''document.querySelectorAll('[name="start_time"]')[0].removeAttribute("readonly")'''
# js = "document.querySelectorAll('#start_time_id')[0].removeAttribute('readonly')" #也可使用id定位
dr.execute_script(js)
startTime = dr.find_element_by_css_selector('[name="start_time"]')
startTime.clear()
now = time.strftime('%Y-%m-%d %H:%M') #当前日期、时间,如果加上秒就是 :%S
startTime.send_keys(now)
# 移除活动结束时间的readonly属性
js = "document.getElementById('end_time_id').removeAttribute('readonly')"
dr.execute_script(js)
endTime = dr.find_element_by_id('end_time_id')
endTime.clear()
end = date.today() + timedelta(10) #日期是当前日期加10天
end2 = str(end) + ' 23:59' #日期转换成字符串格式,再拼接上具体时间
endTime.send_keys(end2)
save = dr.find_element_by_xpath('//input[@value=" 确定 "]') #定位保存按钮
save.click()
#等待一秒钟后获取页面代码,并保存在ecShop.html文件中
time.sleep(1)
# res = dr.page_source
# print(res)
# with open('ecshop.html','w+',encoding='utf-8') as f:
# f.write(res)
#断言
actualResult = '添加成功'
resultPage = dr.find_element_by_xpath('//tbody/tr/td[2]').text
# if resultPage == actualResult:
if actualResult in resultPage :
print('pass')
else:
print('fail')
pip install sshtunnel报错,尝试升级pip
元素定位失败的原因:
1.页面元素未加载完成
2.元素属性值不是唯一的
3.元素属性值是动态变化的
4.元素不在同一个表单中