1. 历史
2004年selenium诞生 seleniumRC js命令
2006年webdriver诞生
2008年selenium与webdriver合并selenium2.0
2016年selenium3.0诞生
2021年selenium4.0诞生
selenium4 统一了各浏览器标准,应该是未来5年内的主流
必须3.7以上,安装报错,多半要升级pip: python -m pip install --upgrade pip
2. 工作原理
- c/s架构,客户端和服务器 通过http请求交换信息 (selenium/webdriver/remote/remote_connection.py 里的_request())
- 通过chromedriver控制浏览器 http://chromedriver.storage.googleapis.com/index.html /usr/local/bin 或项目根目录 或windows的python根目录。
- 每次都有一个对话session和唯一标识 sessionID
3. 常用命令
- driver.get() 打开浏览器进入指定url ###########################
- close和quit 的进程影响
- .get_cookies() 获取全部cookie
- .get_cookie(‘具体名字’) 根据cookie的name查找具体
- .add_cookie() 增加一个cookie
- .delete_all_cookies 删除全部cookie
- delete_cookie(“cookie_name”) 删除指定cookie
- driver.find_element() 定位 (from selenium.webdriver.common.by import By)
- .get_attribute() 获取元素属性
- .forward() 向前
- .back() 后退
- .refresh() 刷新
- .title 获取页面标题
- .get_screenshot_as_file(图片名) 截图
- .maximize_window() 窗口最大化
- .set_window_size(900, 700) 固定窗口大小
- drvier.name 获取浏览器名 ###########################
- .get_window_position 获取当前窗口坐标
- .get_window_size 获取当前窗口长宽
- 元素.location 获取元素
- .size 元素大小
- .is_selected() 是否选中
- .is_displayed() 是否显示
- .is_enabled() 是否禁用
- .text 获取本文
- .send_keys(u’青春’) 输入
- .clear() 清空
- .click() 点击
- .tag_name 元素标签名
- .value_of_css_property(css_name) 获取css属性值
- .sumbit() 提交表单 ###########################
4. 等待
- 隐式
driver.implicitly_wait(10) - 程序睡眠、强制等待
times.sleep(5) - 显式
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 4, 0.5).until(EC.presence_of_element_located((By.ID, “kw”))) #也有until_not
<div id="d">
<button id="kw">我是按钮</button>
</div>
<script>
setTimeout( ()=>{
var k = document.getElementById('kw')
k.remove()
}
,3000
)
</script>
- 过期终止 ##################
driver.set_page_load_timeout(5)
try:
driver.get("")
except:
driver.execute_script("window.stop()")
5. 定位元素
- 单个或多个
- ID
- LINK_TEXT
- PARTIAL_LINK_TEXT
- NAME
- TAG_NAME
- CLASS_NAME
- CSS_SELECTOR 大多数情况下都是在浏览器f12直接复制
driver.find_element(By.CSS_SELECTOR, ‘.a.b.c[value=“”]’)
用来表示id属性
小数点用来表示class属性
[]万能属性可以用来表示所有属性:[id=“”] [class=“”]
如果用的标签名就不要在前面加任何符号 如 input[class=“”]
用来表示父子元素之间的关系 如 div > a
空格表示祖先和子孙之间的关系 如 div#id a.class ###########################
索引法
a:nth-child(3) 索引法,第几个
a:first-child 第一个
a:last-child 第二个
获取目标索引的元素,且必须为a标签或其他指定筛选
获取符合条件的所有元素
如果前面没有父级或祖级,则会获取全页面所有块级内符合元素
- XPATH 大多数情况下都是在浏览器f12直接复制
一般定位
/div/a[1] div下的第一个a
/div/a[last()] div下的最后一个a
/div/a[last()-1] div下倒数第二个a
/div/a[position()<4] 选取div下最前面的3个a
/div[@name] 选取有name属性的div
/div[@name=“kw”] 选取name为kw的div
/div/a[price>50] 选取div下价格大于50的a
绝对定位/ 和相对定位// :相对定位时,系统最优为最末尾标签名在最上的 ######################
文本定位 如 //button[text()=“”]
模糊定位 [starts-with(@id,“s”)] 或 [ends-with(@id,“e”)]
[图片]
逻辑定位and or: //input[@name=“phone” and @name=“email”] ####################
轴定位: 轴名称::节点名称
当前节点后所有节点 //a[@…=“”]/following:😗
当前节点后同级节点 //a[@…=“”]/following-sibling:😗
当前节点所有子节点 //a[@…=“”]/child:😗
当前节点前所有节点 //a[@…=“”]/preceding:😗
当前节点前同级节点 //a[@…=“”]/preceding-sibling:😗
当前节点所有父节点 //a[@…=“”]/parent:😗
当前节点所有祖父节点 //a[@…=“”]/ancestor:😗
###########################
相对定位:
from selenium.webdriver.support.relative_locator import locate_with, with_tag_name
above 元素上
below 元素下
to_left_Of 元素左
to_right_Of 元素右
near 附近
e2 = driver.find_element(locate_with(By.TAG_NAME, "xxxxx").above(e1).to_left_of(e1))
############################
通配符
[图片]
比如 //查找所有元素节点
比如 //[@id] 即查找任何带有id属性的节点
比如 //node()/a 查找各种子元素有a的元素的子元素a
多路径匹配://button | //a
6. 复杂情况
- js改变元素属性
js = 'var q=document.getElementById(\"idddd\");q.style.xxx=(\"xxxx\");'
driver.execute_script(js)
- 元素被遮挡
- 元素要特定触发
- 下拉框
from selenium.webdriver.support.ui import Select
select_by_index() # 通过索引定位;注意:>index索引是从“0”开始。
select_by_value() # 通过value值定位,va>lue标签的属性值。
select_by_visible_text() # 通过文本值定位,即显>示在下拉框的值。
Select(chr.find_element_by_name("姓名")).select_by_index(1) ###############
- alert弹窗
driver.switch_to.alert.text
driver.switch_to.alert.accept() - 内嵌frame
switch_to.frame()
driver.switch_to.parent_frame() - 多标签页
current_window_handle:获取当前窗口的句柄
window_handles:返回当前浏览器的所有窗口的句柄
switch_to_window():用于切换到对应的窗口 - 滚动条
js = “var q=document.documentElement.scrollTop=1200” - 内嵌滚动条
用内部顶层元素拖拽到底部
document.getElementById(‘kw1’).scrollTop=1200
7. 鼠标操作
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(driver).具体动作(值或元素).perform()
- click_and_hold() :点击鼠标左键,不松开 (不写元素也不报错)
- context_click() :点击鼠标右键 (不写元素也不报错)
- double_click() :双击鼠标左键 (不写元素也不报错)
- drag_and_drop(起始元素, 目标元素) :拖拽到某个元素然后松开
- drag_and_drop_by_offset(起始元素, xoffset, yoffset) :拖拽到某个坐标然后松开(如果坐标过大,会引发报错)
- move_by_offset(xoffset, yoffset) :鼠标从当前位置移动到某个坐标 ( 如果坐标过大,会引发报错)
- move_to_element(目标元素) :鼠标移动到某个元素 (如果元素不存在则报错,如果存在但遮挡则不报错)
- release() :释放按住的鼠标 ,(如果之前并未点击鼠标,也不会报错)
8. 键盘操作
from selenium.webdriver.common.keys import Keys
- 常见按钮,更多按键可以在Keys.py查看。
(Keys.CONTROL , 'a') 全选
(Keys.CONTROL , 'c') 赋值
(Keys.CONTROL , 'x') 剪切
(Keys.CONTROL , 'v') 粘贴
(Keys.F1) F1-F12
(Keys.BACK_SPACE) 删除键
(Keys.ENTER) 回车
(Keys.ESCAPE) esc键
(Keys.SPACE) 空格
(Keys.TAB) tab制表键
- 单独用法
ActionChains(driver).send_keys(Keys.ENTER).perform()
ActionChains(driver).send_keys_to_element(el,Keys.ENTER).perform()
driver.find_element_by_id("kw").send_keys(Keys.ENTER')
- 组合用法
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
- 连续用法
ActionChains(driver).send_keys(Keys.TAB).send_keys(Keys.ENTER).perform()
pause(5) 是中间暂停5秒
10. 自定义设置
具体在 options.py
- 添加启动参数
options = webdriver.ChromeOptions()
options.add_argument("具体参数")
driver = webdriver.Chrome(options=options)
无头模式 options.add_argument("headless")
屏幕分辨率(要和无头模式一起使用为佳) options.add_argument('window-size=1920x1670')
不加载任何图 options.add_argument("blink-settings=imagesEnabled=false")
最大化启动:options.add_argument("--start-maximized")
代理(伪装手机)option.add_argument('--user-agent=iphone')
用户配置: user-data-dir= (通过浏览器输入chrome://version/打开后看个人资料路径即可拿到,注意,mac需要删到Google/Chrome,并且需要关闭目前谷歌的本地浏览器)
设置驱动路径
from selenium.webdriver.chrome.service import Service as ChromeService
service = ChromeService(executable_path=你的driver路径)
driver = webdriver.Chrome(service=service)
续session 启动(调试写脚本神器):
- 原理:手动打开一个浏览器,需要指定浏览器的端口,所以你就不能通过点击图标的方式启动,要用命令的方式启动,因为这样可以加端口参数。然后你的所有自动化脚本,全部设置调试域名为这个手动命令启动的浏览器端口。脚本就会自动接着这个浏览器启动。
- 多线程共同作用
接着浏览器启动自动化 (请先用对应端口手动启动浏览器 --remote-debugging-port=6789 )
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_experimental_option("debuggerAddress", "127.0.0.1:6789")
driver = webdriver.Chrome(options=options)
driver.find_element(By.ID,'kw1').send_keys('x')
driver.quit()
数据分离&关键字驱动&闭包动态: (比较常见)
- 媒介:使用excel工作簿,是多维度的
excel表后缀xls的读:xlrd (pip3 install xlrd)
sz=xlrd.open_workbook(fname).sheet_by_index(0) #打开一个工作簿,并用下标获取里面的第一个工作表,
sz=xlrd.open_workbook(fname).sheet_by_name("Sheet1") #打开一个工作簿,并用名称获取里面的某一个工作表
sz.cell_value(1,1)#读取单元格内容
sz._cell_values # 直接拿到二元元组 所有数据
sz.nrows #读取行数
sz.ncols #读取列数
sz.name #读取工作表名字
sz.number # 工作表序号 0开始
- 单数据分离:相较于线性脚本,数据拿出,动态结合。
封装到ttt.py
import xlrd
class XLS():
def __init__(self,fname,index):
self.fname = fname
self.biao = xlrd.open_workbook(fname).sheet_by_index(index)
def get_all_values(self):
return self.biao._cell_values
def get_value(self,y,x):
return self.biao.cell_value(y,x)
test.py:
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from ttt import XLS
xls = XLS('aaa.xls',0)
driver = webdriver.Chrome()
driver.get('http://localhost:63342/7thProject/wqrf.html')
inputs = driver.find_elements(By.TAG_NAME,'input')
for i in range(len(inputs)):
# i = 0,1,2,3,4,5,6...11
# 行 = 0,0,0,1,1,1 除3取整
# 列 = 0,1,2,0,1,2 除3取余
inputs[i].send_keys( xls.get_value(int(i/3),int(i%3)) )
time.sleep(5)
driver.quit()
数据分离+关键字驱动:增加了元素的具体动作和driver的一些常用功能
class PLAY():
# 第一部分:元素的定位。第二部分:元素的动作。第三部分:接收excel表数据并格式化
输入框 = "driver.find_element(By.ID,'kw1')"
按钮 = "driver.find_element(By.ID,'kw2')"
弹窗 = "driver.switch_to.alert"
def __init__(self,元素字符串,值字符串):
self.元素= eval(eval( 'PLAY.%s'%元素字符串 ))
self.值 = 值字符串
def 输入(self):
self.元素.clear()
self.元素.send_keys(self.值)
def 点击(self):
self.元素.click()
def 断言(self):
assert self.元素.text == self.值
self.元素.accept()
all_cells = xls.get_all_values() #二维列表
for i in all_cells:
play = PLAY(i[0],i[2])
exec('play.%s()'%i[1])
time.sleep(1)
数据分离+关键字驱动+unittest闭包动态:
from selenium import webdriver
import time
import unittest
from selenium.webdriver.common.by import By
from ttt import XLS
xls = XLS('aaa.xls',0)
driver = webdriver.Chrome()
driver.get('http://localhost:63342/7thProject/wqrf.html')
class PLAY():
# 第一部分:元素的定位。第二部分:元素的动作。第三部分:接收excel表数据并格式化
输入框 = "driver.find_element(By.ID,'kw1')"
按钮 = "driver.find_element(By.ID,'kw2')"
弹窗 = "driver.switch_to.alert"
def __init__(self,元素字符串,值字符串):
self.元素= eval(eval( 'PLAY.%s'%元素字符串 ))
self.值 = 值字符串
def 输入(self):
self.元素.clear()
self.元素.send_keys(self.值)
def 点击(self):
self.元素.click()
def 断言(self):
assert self.元素.text == self.值
self.元素.accept()
class TEST(unittest.TestCase):
def template(self,i):
play = PLAY(i[0], i[2])
exec('play.%s()'%i[1])
time.sleep(2)
def demo(i):
def func(self):
TEST.template(self,i)
return func
all_cells = xls.get_all_values() #二维列表
for i in range(len(all_cells)):
setattr(TEST,'test_%04d'%i,demo(all_cells[i]))
unittest.main()
time.sleep(3)
driver.quit()
关键字封装
弹窗处理:弹窗断言 + 弹窗关闭
句柄切换:值为下标
页面操作:关闭,前进,后退。
鼠标: 值为具体操作
键盘:值为具体操作
from selenium import webdriver
import time
import unittest
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from ttt import XLS
xls = XLS('aaa.xls',0)
driver = webdriver.Chrome()
driver.get('http://localhost:63342/7thProject/wqrf.html')
# #
class PLAY():
# 第一部分:元素的定位。第二部分:元素的动作。第三部分:接收excel表数据并格式化
输入框 = "driver.find_element(By.ID,'kw1')"
按钮 = "driver.find_element(By.ID,'kw2')"
超链接 = "driver.find_element(By.ID,'kw3')"
def __init__(self,元素字符串,值字符串):
if 元素字符串:
self.元素= eval(eval( 'PLAY.%s'%元素字符串 ))
self.值 = 值字符串
def 输入(self):
self.元素.clear()
self.元素.send_keys(self.值)
def 点击(self):
self.元素.click()
def 弹窗断言(self):
assert driver.switch_to.alert.text == self.值
driver.switch_to.alert.accept()
def 句柄跳转(self):
driver.switch_to.window(driver.window_handles[-1])
def 页面操作(self):
if self.值 == '关闭':
driver.close()
elif self.值 == '向前':
driver.forward()
elif self.值 == '向后':
driver.back()
def 鼠标(self):
if self.值 == '右键':
ActionChains(driver).context_click().perform()
def 键盘(self):
if '粘贴' == self.值.split('-')[1]:
元素 = eval(eval( 'PLAY.%s'%(self.值.split('-')[0]) ))
元素.send_keys(Keys.COMMAND , 'v')
class TEST(unittest.TestCase):
def template(self,i):
play = PLAY(i[0], i[2])
exec('play.%s()'%i[1])
time.sleep(1)
@classmethod
def tearDownClass(cls) -> None:
time.sleep(4)
driver.quit()
def demo(i):
def func(self):
TEST.template(self,i)
return func
all_cells = xls.get_all_values() #二维列表
for i in range(len(all_cells)):
setattr(TEST,'test_%04d'%i,demo(all_cells[i]))
unittest.main()
po模式:
from selenium import webdriver
import time
import unittest
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from ttt import XLS
from ttt import GET_ELEMENT
xls = XLS('aaa.xls',0)
get_element = GET_ELEMENT()
driver = webdriver.Chrome()
driver.get('http://localhost:63342/7thProject/wqrf.html')
class PLAY():
def __init__(self,页面字符串,元素字符串,值字符串):
if 元素字符串:
self.元素= eval(eval( 'get_element.%s("%s")'%(页面字符串,元素字符串) ))
self.值 = 值字符串
def 输入(self):
self.元素.clear()
self.元素.send_keys(self.值)
def 点击(self):
self.元素.click()
def 弹窗断言(self):
assert driver.switch_to.alert.text == self.值
driver.switch_to.alert.accept()
def 句柄跳转(self):
driver.switch_to.window(driver.window_handles[-1])
def 页面操作(self):
if self.值 == '关闭':
driver.close()
elif self.值 == '向前':
driver.forward()
elif self.值 == '向后':
driver.back()
def 鼠标(self):
if self.值 == '右键':
ActionChains(driver).context_click().perform()
def 键盘(self): # 首页-输入框-粘贴
if '粘贴' == self.值.split('-')[-1]:
元素 = eval(eval( 'get_element.%s("%s")'%(self.值.split('-')[0],self.值.split('-')[1]) ))
元素.send_keys(Keys.COMMAND , 'v')
class TEST(unittest.TestCase):
def template(self,i):
play = PLAY(i[0],i[1], i[3])
exec('play.%s()'%i[2])
time.sleep(1)
@classmethod
def tearDownClass(cls) -> None:
time.sleep(4)
driver.quit()
def demo(i):
def func(self):
TEST.template(self,i)
return func
all_cells = xls.get_all_values() #二维列表
for i in range(len(all_cells)):
setattr(TEST,'test_%04d'%i,demo(all_cells[i]))
unittest.main()
import xlrd
class XLS():
def __init__(self,fname,index):
self.fname = fname
self.biao = xlrd.open_workbook(fname).sheet_by_index(index)
def get_all_values(self):
return self.biao._cell_values[1:]
def get_value(self,y,x):
return self.biao.cell_value(y,x)
def get_col(self):
return self.biao.ncols
def get_row(self):
return self.biao.nrows
class GET_ELEMENT():
def 首页(self,s):
d = {
"输入框" : "driver.find_element(By.ID,'kw1')",
"按钮" : "driver.find_element(By.ID,'kw2')",
"超链接" : "driver.find_element(By.ID,'kw3')",
}
return d[s]
def 第二页(self,s):
d = {
"输入框" : "driver.find_element(By.ID,'kw2-1')",
"按钮" : "driver.find_element(By.ID,'kw2-2')",
}
return d[s]