selenium执行js代码的两个方法你都会用吗?


在使用selenium做web自动化的时候,很多小伙伴反馈有些页面上动作我们无法通过selenium封装的方法直接去做,比如说修改元素的属性,影子节点的操作等等。需要使用原生的js代码去实现,而selenium也给我提供了两个执行js代码的方法,一个是execute_script,另一个是execute_async_script。很多小伙伴不太清楚这两个方法有什么区别。那么今天就来和大家聊聊这两个方法的区别以及使用。

execute_script方法

execute_script这个方法应该是大家用的比较多的,接下来我们来看一下这个方法的源码,源码参考如下:

def execute_script(self, script, *args):
"""
Synchronously Executes JavaScript in the current window/frame.
:Args:
- script: The JavaScript to execute.
- \*args: Any applicable arguments for your JavaScript.
:Usage:
driver.execute_script('return document.title;')
"""
converted_args = list(args)
command = None
if self.w3c:
command = Command.W3C_EXECUTE_SCRIPT
else:
command = Command.EXECUTE_SCRIPT
return self.execute(command, {
'script': script,
'args': converted_args})['value']

通过源码的中的使用案例我们可以看到这个方法使用起来是比较简单的,通过script执行传入js代码即可,那么这个方法还有一个不定长参数args,这个参数可以用来传递一些在执行js代码的时候需要的一些参数,比如通过js去操作某个元素,我们可以将定位到的元素传进去,下面我们通过一个案例来看一下:

案例:12306日期修改

打开12306首页大家可以去试一下,图中的日期输入框是不能够直接通过输入修改日期的,原因是input元素有一个readonly属性(只读属性),要修改日期则需要把readonly这个属性设为false,而selenium中没有直接的方法可以修改元素的属性,只有通过js才能做的,那么接下来我们就使用execute_script方法来执行对应的js代码。


代码如下:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.implicitly_wait(30)
driver.get('https://www.12306.cn/index/')
# 点击往返
WebDriverWait(driver, 10, 0.5).until(
EC.element_to_be_clickable(
(By.XPATH, '//div[@class="search-tab-hd"]//a[text()="往返"]'))).click()
# 定位起始时间和终止时间输入框
start_date = driver.find_element_by_id('go_date')
end_date = driver.find_element_by_id('from_date')
# 准备js代码,通过js输入起始时间和终止时间,
js = """
var s_ele = arguments[0];
var e_ele = arguments[1];
s_ele.readonly = false;
s_ele.value ='2020-05-15';
e_ele.readonly=false;
e_ele.value ='2020-06-15';
return [s_ele.value,e_ele.value]
"""
# js中的arguments[0]接收的是args中的第1个参数,就是下面传入的start_date
# js中的arguments[1]接收的是args中的第2个参数,就是下面传入的end_date
# js代码中可以通过return来返回js代码执行之后的结果
# 执行js代码
res = driver.execute_script(js,start_date,end_date)
driver.quit()
那么关于execute_script这个方法的使用我们就先聊到这里,接下来我们来看看另一个方法,
execute_async_script方法
关于execute_async_script这个方法,我们依然先来看看这个方法的源码,源码参考如下:
def execute_async_script(self, script, *args):
"""
Asynchronously Executes JavaScript in the current window/frame.
:Args:
- script: The JavaScript to execute.
- \*args: Any applicable arguments for your JavaScript.
:Usage:
script = "var callback = arguments[arguments.length - 1]; " \
"window.setTimeout(function(){ callback('timeout') }, 3000);"
driver.execute_async_script(script)
"""
converted_args = list(args)
if self.w3c:
command = Command.W3C_EXECUTE_SCRIPT_ASYNC
else:
command = Command.EXECUTE_ASYNC_SCRIPT
return self.execute(command, {
'script': script,
'args': converted_args})['value']

通过源码的注释中我们可以看到,这是一个异步执行js代码的方法,注意:这边的异步执行并不是python中异步执行,而是js代码执行是异步执行的 ,( 关于js异步这边不做过多的扩展,大家可以自行扩展学习),我们来看一下这个方法怎么使用。首先看源码中的使用示例,我们一起来分析一下,

script = """
var callback = arguments[arguments.length - 1];
window.setTimeout(function(){ callback('timeout') }, 3000);
"""
driver.execute_async_script(script)
# 首先来看js中第一行代码 var callback = arguments[arguments.length - 1];
# 这里是将arguments中的最后一个参数获取出来,那么最后一个参数是什么呢?源码中看不到,这边跟大家解释一下,
# callback接受到的是一个返回数据的函数,当我们通过execute_async_script执行js语句之后,可以通过这个方法来返回内容。
# 然后再来分析一下js中第二行代码,设置了一个一秒钟之后异步执行的函数,函数的内部是执行了callback来返回一个数据。

那么接下来我们还是通过12306这个案例来演示,异步js代码的执行:

案例代码:

"""
============================
Author:柠檬班-木森
Time:2020/5/7 16:25
E-mail:3247119728@qq.com
Company:湖南零檬信息技术有限公司
============================
"""
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.implicitly_wait(30)
driver.get('https://www.12306.cn/index/')
# 点击往返
time.sleep(1)
WebDriverWait(driver, 10, 0.5).until(
EC.element_to_be_clickable((By.XPATH, '//div[@class="search-tab-hd"]//a[text()="往返"]'))).click()
# 定位起始时间和终止时间输入框
start_date = driver.find_element_by_id('go_date')
end_date = driver.find_element_by_id('from_date')
js = """
const callback = arguments[arguments.length - 1]
var s_ele = arguments[0];
var e_ele = arguments[1];
s_ele.readonly = false;
s_ele.value ='2020-05-15';
setTimeout(function() {
e_ele.value ='2020-06-15';
callback('修改成功')
}, 3000);
e_ele.readonly=false;
e_ele.value ='2020-07-15';
"""
# 输入起始时间和终止时间
res = driver.execute_async_script(js,start_date,end_date)
print(res)
time.sleep(10)
driver.quit()
# 运行结果:
# print(res) 打印出来的内容为:修改成功
# 页面日期设置的顺序则是:
# 先设置起始日期2020-05-15,
# 开启异步执行的函数(3秒之后执行)
# 再设置终止日期2020-07-15,
# 3秒钟之后
# 设置终止日期2020-06-15
# callback返回: 修改成功

效果图如下: