selenium執行js代碼的兩個方法你都會用嗎?
轉發:https://zhuanlan.zhihu.com/p/139192010
在使用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返回: 修改成功
效果圖如下:
