页面上的操作有时通过Selenium是无法实现的,如滚动条、时间控件等,此时就需要借助JavaScript来完成。
WebDriver提供了一个内置方法来操作JavaScript,代码如下:
driver.execute_script(self, script, args)
可以通过两种方式在浏览器中执行JavaScript。
1、在文档根级别执行JavaScript
在文档根级别下,使用JavaScript提供的方法捕获想要的元素,然后声明一些操作并使用web Driver执行此JavaScript。例如:
JJScript = "document.getElementsByName('input')[1].click();"
driver.execute_script(JJScript)
2、在元素级别执行JavaScript
在元素级别下,使用webdriver捕获想要使用的元素,然后使用JavaScript声明一些操作并通过将web元素作为参数传递给JavaScript来使用webdriver执行此JavaScript。例如:
BtnName = driver.find_element_by_xpath("//*[@id='sb_form_go']")
driver.execute_script("arguments[0].click;", BtnName)
程序说明:
(1)通过webdriver提供的XPath方法捕获元素,代码如下:
BtnName = driver.find_element_by_xpath("//*[@id='sb_form_go']")
(2)声明JavaScript并对元素执行单击操作,代码如下:
arguments[0].click
(3)通过execute_script()使用的JavaScript语句作为字符串 值调用方法,代码如下:
driver.execute_script("arguments[0].click;", BtnName)
当有多个JS操作时,可以书写如下操作:
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
SearchName = driver.find_element_by_xpath('//*[@id="kw"]')
BtnName = driver.find_element_by_xpath('//*[@id="su"]')
driver.execute_script("arguments[0].value='bella'; arguments[1].click();", SearchName, BtnName)
sleep(3)
driver.quit()
注:一般来说,不建议通过JavaScript来执行selenium已经至耻的功能。selenium已有功能比JavaScript更完善,例如在操作元素、获取元素的值等方面,都会有更全面的验证机制(例如判断元素是否存在、是否不为0、是否处于可操作性状态等),完全没有必要使用没有验证机制所以会照样操作,所以会出现非正常的测试结果。
1、JavaScript操作日期控件
日期控件在网站上经常遇到,如12306网站、旅游订票网站(携程、去哪儿等)、租车网站(如神州租车)等。
1.1、常规日期控件的操作
下面用12306网站为例进行介绍,如图:
当通过send_keys给时间控件赋值时,看到只是把时间控件打开了,并没有选择设定的日期。采用JS赋值,则可以完成对时间控件的操作,代码如下:
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.12306.cn/index/")
# driver.find_element_by_xpath('//*[@id="train_date"]').send_keys("2024-08-30")
DateJS = "document.getElementById('train_date').value = '2024-08-30'"
driver.execute_script(DateJS)
sleep(3)
driver.quit()
用send_keys()结果:
用JS的结果:
2、JavaScript控制浏览器滚动条
当访问页面上的展现结果超过一屏时,如果想浏览或操作屏幕下半部分的内容时,由于被屏幕遮挡,因此无法操作对应的元素。这时就需要借助滚动条来拖动屏幕,实现浏览更多的内容或使被操作的元素展现在屏幕上。
滚动条是无法直接被定位到的,webdriver中也没有直接的方法控制滚动条。此时遍需要借助JavaScript来操作滚动条。
2.1、控制滚动条上下滑动
代码如下:
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.set_window_size(800, 600)
driver.find_element_by_xpath("//*[@id='kw']").send_keys("bella")
driver.find_element_by_xpath("//*[@id='su']").click()
sleep(4)
js = "var q=document.documentElement.scrollTop = 10000" # 滚动条到底部
driver.execute_script(js)
sleep(3)
ks = "var a=document.documentElement.scrollTop = 0" # 滚动条回到顶部
driver.execute_script(ks)
sleep(4)
driver.quit()
注:JS中可以修改scroll Top的值来定位浏览器右侧滚动条的位置,0表示最上面,10000表示最底部,scrollTop的值介于0-10000之间。
2.2、控制横向滚动条
通过scrollTo(x,y)来实现横向与纵向滚动条的移动,x是横坐标,y是纵坐标。y为0时,x设置任意数,便是滚动条左右滚动的情况。
代码如下:
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.set_window_size(800, 600)
driver.find_element_by_xpath("//*[@id='kw']").send_keys("bella")
driver.find_element_by_xpath("//*[@id='su']").click()
sleep(4)
Js = "window.scrollTo(100,0)"
driver.execute_script(Js)
sleep(4)
driver.quit()
效果图:
scrollTo函数还可以通过其他方式来实现滚动条。
- scrollHeight:获取浏览器的滚动高度;
- scrollWidth:获取刘阿龙年起的股东你宽度;
- scrollLeft:设置或获取窗口可见内容与最左端之间的距离;
- scrollTop:设置或获取窗口可见内容与最顶端之间的距离;
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.set_window_size(800, 600)
driver.find_element_by_xpath("//*[@id='kw']").send_keys("bella")
driver.find_element_by_xpath("//*[@id='su']").click()
sleep(4)
# 滚动条到最底部
DownJs = "window.scrollTo(0,document.body.scrollHeight)"
driver.execute_script(DownJs)
sleep(2)
# 滚动条回到初始位置
InitJs = "window.scrollTo(0,0)"
driver.execute_script(InitJs)
sleep(2)
# 滚动调到最左侧
LeftJs = "window.scrollTo(document.body.scrollWidth,600)"
driver.execute_script(LeftJs)
sleep(2)
# 滚动条回到初始位置
driver.execute_script(InitJs)
sleep(4)
driver.quit()
3、引入JavaScript库处理HTML5拖曳
首先,先编写个测试页面,该页面完全使用HTML5原生支持拖曳功能,页面代码如下:
<html>
<head>
<style type="text/css">
#div1{
width:300px;
height:100px;
padding:10px;
border:1px solid #aaaaaa
}
</style>
<script type="text/javascript">
function allowDrop(ev){
ev.preventDefault();
}
function drag(ev){
ev.dataTransfer.setData("Text", ev.target.id);
}
function drop(ev){
ev.preventDefault();
var data = ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
alert("图片放置成功");
}
</script>
</head>
<body>
<p>请把图片拖放到方框中:</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<br/>
<img id="drag1" src="https://cdn.ptpress.cn/pubcloud/3/app/0718A6B0/cover/20191204BD54009A.png" draggable="true" ondragstart="drag(event)">
</body>
</html>
引用第三方库有两种方式,先说第一种,引用方式是在网页中嵌入<script>标签,让<script>
标签的src库指向第三方库。但由于浏览器加载<script>中的JavaScript时是异步加载的,因此用这种方式引用外部库时,必须要等待库加载完毕才能执行下一步操作。
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Firefox()
driver.get("file:///F:/TestDragAndDrop.html")
injectJSTemplate = "var injectionScript = document.createElement('script'); injectionScript.src = arguments[0]; document.getElementsByTagName('head')[0].appendChild(injectionScript)"
jQueryLibPath = "https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"
driver.execute_script(injectJSTemplate, jQueryLibPath)
WebDriverWait(driver, 10).until(lambda d: d.execute_script("return typeof(jQuery)!='undefined';"))
dndLibPath = "http://blog-static.cnblogs.com/files/realdigit/drag_and_drop_helper.js"
driver.execute_script(injectJSTemplate, dndLibPath)
WebDriverWait(driver, 10).until(lambda d: d.execute_script("return typeof(drag_and_drop_loaded)!='undefined';"))
driver.execute_script("$('#drag1').simulateDragDrop({dropTarget: '#div1'});")
第二种引用方式就是直接加载并执行第三方库的内容,这种方式属于同步加载,因此不需要在引用各个JavaScript库时等待加载完毕。整体而言,第二种方式比第一种方式简洁。
from selenium import webdriver
import urllib.request
driver = webdriver.Firefox()
driver.get("file:///F:/TestDragAndDrop.html")
jQueryLibPath = "https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"
with urllib.request.urlopen(jQueryLibPath) as response:
dnd_all_javascript = response.read().decode('utf-8')
driver.execute_script(dnd_all_javascript)
dndLibPath = "http://blog-static.cnblogs.com/files/realdigit/drag_and_drop_helper.js"
with urllib.request.urlopen(dndLibPath) as response:
dnd_all_javascript = response.read().decode('utf-8')
driver.execute_script(dnd_all_javascript)
driver.execute_script("$('#drag1').simulateDragDrop({dropTarget: '#div1'});")
注:引入外部JavaScript库可能会对现有功能造成影响,例如造成变量或函数名称冲突,原型被意外修改等,导致网站功能异常,因此引入之前需要仔细评估该库对网站的影响。
若有同学会JavaScript其他的功能欢迎补充。