五. WebElement 物件進階用法 :
在前一篇測試中演示了用 Selenium 從簡單網頁中擷取資料與操控檔案上傳的方法, 本篇則要進一步來操作較複雜的網頁與使用其他元素定位方法.
1. 操控選項元素 radio/checkbox/select 與彈出視窗 :
HTML 選項元素有 radio (選項圓鈕), checkbox (核取方塊), 與 select option (下拉式選單) 三種, 除了 checkbox 為多選外, 其他為單選. 包含這三種選項元素之測試網頁如下 :
網頁原始碼如下 :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<title></title>
</head>
<body>
<form action="" id="poll_form">
<p>最喜歡的韓星 :</p>
<input type="radio" value="金智媛" name="idol">
<label for="idol-1">金智媛</label>
<input type="radio" value="朴恩斌" name="idol">
<label for="idol-2">朴恩斌</label>
<input type="radio" value="孫藝珍" name="idol">
<label for="idol-3">孫藝珍</label>
<input type="radio" value="李世榮" name="idol">
<label for="idol-4">李世榮</label>
<input type="radio" value="金裕貞" name="idol">
<label for="idol-5">金裕貞</label><br>
<p>推薦的韓劇 :</p>
<input type="checkbox" name="drama" value="揹著善宰跑">
<label for="drama-1">揹著善宰跑</label>
<input type="checkbox" name="drama" value="淚之女王">
<label for="drama-2">淚之女王</label>
<input type="checkbox" name="drama" value="寄生獸">
<label for="drama-3">寄生獸</label>
<input type="checkbox" name="drama" value="低谷醫生">
<label for="drama-4">低谷醫生</label>
<p>最喜歡的平台 :</p>
<select id="platform">
<option value="Netflix">Netflix</option>
<option value="Disney">Disney</option>
<option value="愛奇藝">愛奇藝</option>
<option value="Line TV">Line TV</option>
</select>
<input type="button" value="提交" id="ok">
<div id="result"></div>
</form>
<script>
$(document).ready(function(){ // 注意, 不要用簡寫的 $(function(){
$("#ok").click(function(){
idol=$("input[name='idol']:checked").val()
var drama=[];
$("[name=drama]:checkbox:checked").each(function(){
drama.push($(this).val());
});
platform=$("#platform").val();
msg="最喜歡的韓星: " + idol + "\n" +
"推薦的韓劇: " + drama.join() + "\n" +
"最喜歡的平台: " + platform;
alert(msg);
msg=msg.replace(/\n/g, "<br>"); # 將 \n 跳行字符改成網頁的 <br>
$("#result").html(msg); # 將 msg 插入 div 內容中
});
});
</script>
</body>
</html>
此處使用 jQuery 的前端程式碼來模擬表單提交後的回應功能 (當然如果有後端伺服器來做回應更好), 故表單的 action 並未指定後端處理程式, 當按下 "提交" 鈕後會觸發執行前端 Javascript 程式碼而非提交表單給後端.
注意, 這裡在檢測網頁是否載入時要用標準的 $(document).ready(function(){, 不要用簡寫的 $(function(){, 當然手動操作兩種寫法都可以, 但後續測試發現 Selenium 的 Javascript 執行器似乎只認得標準寫法 (用簡寫會出現錯誤), 所以如果目標網頁是用簡寫那就沒辦法了
上面網頁手動操作結果如下 :
按彈出視窗的 "確定" 鈕後會在網頁下方出現選項結果 :
接下來用 Selenium 模擬上面的手動操作 :
>>> from selenium import webdriver
>>> from selenium.webdriver.common.by import By
>>> driver=webdriver.Firefox()
>>> driver.implicitly_wait(20)
>>> url='https://tony1966.github.io/test/python/web_crawler/crawler_test_3.htm'
>>> driver.get(url)
載入目標網頁後, 要模仿上面人工操作勾選三種選項元素, "最喜歡的韓星" 為 5 選 1 的單選, 我們要勾選第一個選項 "金智媛", 觀察上面的原始碼發現這 5 個選項都有 name='idol' 屬性, 這在 HTML 中是必須的, 單選的選項必須綁定相同 name 才會有單選效果, 因此可以呼叫 find_elements() 方法並傳入 By.NAME 或字串 "name" 來取得所有 name="idol" 的物件串列, 再用索引 [0] 來定位 "金智媛" 這個 input 元素選項, 最後呼叫此物件之 click() 方法點選它 :
>>> radios=driver.find_elements('name', 'idol') # 搜尋所有具有 name='idol' 的元素
>>> len(radios) # radio 單選圓鈕有 5 個
5
>>> radios[0].get_attribute('value') # 確認第一個選項是 "金智媛"
'金智媛'
>>> radios[0].click() # 點選第一個選項
結果如下 :
接下來要勾選多選的 "推薦的韓劇" 選項, 此處我們要勾選 "淚之女王" 與 "寄生獸" 這兩項, 雖然 checkbox 元素並未像 radio 那樣必須綁定相同的 name 屬性值 (基本上多選意味著每個選項都是獨立的), 但取相同的 name 可表示他們是同一組的選項, 這樣要定位與選取都很方便, 例如 :
>>> checkboxes=driver.find_elements('name', 'drama')
>>> len(checkboxes)
4
但如果 checkbox 元素沒有 name 屬性, 則可以使用 CSS 選擇器來定位, 例如 :
>>> checkboxes=driver.find_elements('css selector', "input[type='checkbox']")
>>> len(checkboxes)
4
如果 checkbox 還有其他組, 使得從中找出目標選項之索引有點麻煩的話, 只好使出絕招 : XPATH. 注意, 如果有匯入 selenium.webdriver.common.by.By 類別, find_elements() 的第一參數可以用 By.NAME 或 By.CSS_SELECTOR.
我們要勾選的是索引 [1] 的 "淚之女王" 與索引 [2] 的 "寄生獸" :
>>> checkboxes[1].click()
>>> checkboxes[2].click()
到這一步結果如下 :
然後是下拉式選單 select option 元素, 我們要在選項中點選 "Disney" 為最喜歡的平台, 此例中只有一組 select option, 所以可以用 "tag name" 來搜尋全部 option :
>>> options=driver.find_elements('tag name', 'option')
>>> len(options)
4
當然也可以用 'css selector' :
>>> options=driver.find_elements('css selector', 'option')
>>> len(options)
4
我們要勾選的是索引 [1] 的 "Disney" :
>>> options[1].click()
這樣三個選項都用 Selenium 勾選好了, 結果如下 :
最後是按下 "提交" 鈕, 從原始碼可知它有一個 id="ok" 的屬性, 故可使用 By.ID 或 "id" 來搜尋它, 如果提交按鈕沒有 id, name, 或 class 等可明確定位的屬性, 那就只好使用 XPATH 了.
>>> ok_btn=driver.find_element('id', 'ok') # 搜尋 id-"ok" 的元素
>>> ok_btn.click()
Firefox 瀏覽器結果如下 :
最後來研究要如何關閉 Javascript 的 window.alert() 開啟的彈出視窗, 這有兩個方法可達成, 第一個方法是透過 selenium.webdriver.common.alert.Alert 類別來取得對應於彈出視窗的 Alert 物件, 先來檢視 selenium.webdriver.common.alert 模組的內容 :
>>> import selenium
>>> dir(selenium.webdriver.common.alert)
['Alert', 'Command', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'keys_to_typing']
>>> type(selenium.webdriver.common.alert.Alert)
<class 'type'> # Alert 是一個類別
只要將 WebDriver 物件傳給 Alert 類別的建構式 Alert() 即可建立對應於瀏覽器彈出視窗的 Alert() 物件, 先匯入 Alert 類別 :
>>> from selenium.webdriver.common.alert import Alert
>>> alert=Alert(driver) # 建立彈出視窗的 Alert 物件
檢視 Alert 物件內容 :
>>> dir(alert)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'accept', 'dismiss', 'driver', 'send_keys', 'text']
其中 text 屬性可取得彈出視窗之內容 :
>>> alert.text
'最喜歡的韓星: 金智媛\n推薦的韓劇: 淚之女王,寄生獸\n最喜歡的平台: Disney'
而 accept() 或 dismiss() 方法即可用來關閉彈出視窗 :
>>> alert.accept() # 或 dismiss()
這樣彈出視窗就被關閉了. 結果如下 :
但 Javascript 彈出視窗並不只是像此例中的 Info 視窗而已, 還有讓使用者輸入資料的 Prompt 視窗, 若要模擬使用者的填入動作可以呼叫 send_keys() 方法, 參考 :
關掉彈出視窗的第二個方法是利用 WebDriver 物件的 switch_to 類別 :
>>> dir(driver.switch_to)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_driver', '_w3c_window', 'active_element', 'alert', 'default_content', 'frame', 'new_window', 'parent_frame', 'window']
以 driver.switch_to.alert 可以取得一個 Alert 物件 :
>>> alert=driver.switch_to.alert
>>> type(alert)
<class 'selenium.webdriver.common.alert.Alert'>
用 dir() 檢視 Alert 物件內容 :
>>> dir(alert)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'accept', 'dismiss', 'driver', 'send_keys', 'text']
可見用這方法所建立的 Alert 物件與上面用 selenium.webdriver.common.alert.Alert 類別所建立的是一樣的. 呼叫其中的 accept() 或 dismiss() 方法即可關閉彈出視窗 :
>>> alert.accept() # 或 alert.dismiss() 亦可關閉視窗
執行後彈出視窗果然倍關閉, 結果如下 :
以上測試演示了如何利用 Selenium 操控瀏覽器勾選三種選項, 以及如何關閉彈出視窗的方法, 完整程式碼如下 :
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver=webdriver.Firefox()
driver.implicitly_wait(20)
url='https://tony1966.github.io/test/python/web_crawler/crawler_test_3.htm'
driver.get(url)
radios=driver.find_elements('name', 'idol')
radios[0].click()
checkboxes=driver.find_elements('name', 'drama')
checkboxes[1].click()
checkboxes[2].click()
options=driver.find_elements('tag name', 'option')
options[1].click()
ok_btn=driver.find_element('id', 'ok')
ok_btn.click()
time.sleep(10)
alert=driver.switch_to.alert
alert.accept()
time.sleep(10)
driver.close()
此處使用 time.sleep() 讓瀏覽器操控畫面暫停以利觀察結果, 否則會因執行速度很快看不到.
2. 使用 Select 類別操控單選與多選 select 元素 :
在上面的測試中為了簡單起見指測試了單選 select 元素 (又稱為下拉式選單), 如果 select 元素設定了 multiple 屬性則會變成可多選 (按住 Ctrl 鈕用滑鼠點選, 因會展開全部選項又稱為清單元件), 這兩種 select 元素都可以呼叫其 option 元素物件之 click() 方法來模擬真人點選動作, 也可以用 Selenium 提供的 selenium.webdriver.support.select.Select 類別來操作.
我準備了如下的網頁來測試 :
其實就是將上面測試的 radio 單選改成 select 單選; 將 checkbox 複選改成 select 複選而已, 網頁原始碼如下 :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<title></title>
</head>
<body>
<form action="" id="poll_form">
<span>最喜歡的韓星 :</span>
<select id="idol">
<option value="金智媛">金智媛</option>
<option value="朴恩斌">朴恩斌</option>
<option value="孫藝珍">孫藝珍</option>
<option value="李世榮">李世榮</option>
<option value="金裕貞">金裕貞</option>
</select><br>
<span>推薦的韓劇 :</span>
<select id="drama" multiple>
<option value="揹著善宰跑">揹著善宰跑</option>
<option value="淚之女王">淚之女王</option>
<option value="寄生獸">寄生獸</option>
<option value="低谷醫生">低谷醫生</option>
</select><br>
<input type="button" value="提交" id="ok">
<div id="result"></div>
</form>
<script>
$(document).ready(function(){
$("#ok").click(function(){
var idol=$("#idol").val()
var drama=$("#drama").val();
msg="最喜歡的韓星: " + idol + "\n" +
"推薦的韓劇: " + drama + "\n";
alert(msg);
msg=msg.replace(/\n/g, "<br>");
$("#result").html(msg);
});
});
</script>
</body>
</html>
此處仍然使用 jQuery 在本地端模擬表單提交至後端的結果, 但勾選的結果是顯示在表單最底下 id='result' 的 div 元素中, 所以表單的 action 並未指定後端處理程式, 當按下 "提交" 鈕後會觸發執行前端 Javascript 程式碼而非提交表單給後端伺服器.
注意, 不管是單選或多選的 select, 用 jQuery 取得被勾選之項目值都是使用 $("#select_id").val(), 對單選之下拉式選單而言傳回被勾選 option 元素之 value 屬性值; 對多選的清單而言則是傳回全部勾選 option 元素之 value 屬性值組成之陣列.
上面的網頁勾選後按 "提交" 鈕結果如下 :
接下來用 Selenium 模擬上面的手動操作 :
首先匯入模組與類別 :
>>> from selenium import webdriver
>>> from selenium.webdriver.firefox.options import Options
由於 GitHub Page 似乎已有防爬蟲機制 (奇怪的是之前測試上面的範例時沒傳送 User Agent 也沒問題哩), 如果沒有傳送 User Agent 會無法連線, 因此這裡匯入 Options 類別來添加 HTTP 標頭, 先建立 Options 物件 :
>>> options=Options()
然後設定 User Agent 字串並呼叫 Options 物件的 add_argument() 方法時將 User Agent 屬性加入 HTTP 標頭中 :
>>> ua='Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0'
>>> options.add_argument(f'user-agent={ua}'
然後呼叫建立 WebDriver 物件時將 Options 物件傳給 Firefox() 建構式的 options 參數 :
>>> driver=webdriver.Firefox(options=options)
>>> driver.implicitly_wait(20)
這樣呼叫 driver.get() 時便能順利連線網頁了 :
>>> url='https://tony1966.github.io/test/python/web_crawler/crawler_test_4.htm'
>>> driver.get(url)
接下來先用 WebElement 物件的 click() 來模擬勾選選項的動作, 先在單選的最受歡迎韓星下拉式選單中勾選第一個 "金智媛" :
>>> idol=driver.find_element('id', 'idol') # 搜尋 id=idol 之元素 (select)
>>> idol_options=idol.find_elements('tag name', 'option') # 搜尋旗下之 option 元素
>>> len(idol_options) # 有 5 個選項
5
>>> idol_options[0].click() # 勾選第一個
接下來在 "推薦的韓劇" 中勾選 "淚之女王" 與 "寄生獸" :
>>> drama=driver.find_element('id', 'drama') # 搜尋 id=drama 之元素 (select)
>>> drama_options=drama.find_elements('tag name', 'option') # 搜尋旗下之 option 元素
>>> len(drama_options) # 有 4 個選項
4
>>> drama_options[1].click() # 勾選第 2 個
>>> drama_options[2].click() # 勾選第 3 個
最後按下 "提交" 鈕 :
>>> ok_btn=driver.find_element('id', 'ok')
>>> ok_btn.click()
>>> driver.close()
結果與上面手動操作相同.
完整程式碼如下 :
# selenium_select_option_1.py
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
import time
options=Options()
ua='Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0'
options.add_argument(f'user-agent={ua}')
driver=webdriver.Firefox(options=options)
driver.implicitly_wait(20)
url='https://tony1966.github.io/test/python/web_crawler/crawler_test_4.htm'
driver.get(url)
idol=driver.find_element('id', 'idol')
idol_options=idol.find_elements('tag name', 'option')
idol_options[0].click()
drama=driver.find_element('id', 'drama')
drama_options=drama.find_elements('tag name', 'option')
drama_options[1].click()
drama_options[2].click()
ok_btn=driver.find_element('id', 'ok')
ok_btn.click()
time.sleep(5)
driver.close()
接下來我們改用 Select 類別來操作下拉式選單 (單選) 與清單 (複選), 此類別收錄在 selenium.webdriver.support 模組內 :
>>> import selenium
>>> dir(selenium.webdriver.support)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'relative_locator', 'select']
>>> dir(selenium.webdriver.support.select)
['By', 'List', 'NoSuchElementException', 'Select', 'UnexpectedTagNameException', 'WebElement', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
使用時可直接匯入 Select 類別 :
>>> from selenium.webdriver.support.select import Select
載入上面測試網頁後取得兩個 select 元素的 WebElement 物件後將他們傳給 Select 類別的建構式 Select(), 它會傳回一個代表 select 元素的 Selectt 物件 :
>>> idol=driver.find_element('id', 'idol') # 搜尋 id=idol 之元素 (select)
>>> drama=driver.find_element('id', 'drama') # 搜尋 id=drama 之元素 (select)
>>> idol_select=Select(idol) # 建立 Select 物件
>>> drama_select=Select(drama) # 建立 Select 物件
>>> type(idol_select)
<class 'selenium.webdriver.support.select.Select'>
>>> type(drama_select)
<class 'selenium.webdriver.support.select.Select'>
檢視 Select 物件內容 (以 idol_select 為例) :
>>> dir(idol_select)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_el', '_escape_string', '_get_longest_token', '_set_selected', '_unset_selected', 'all_selected_options', 'deselect_all', 'deselect_by_index', 'deselect_by_value', 'deselect_by_visible_text', 'first_selected_option', 'is_multiple', 'options', 'select_by_index', 'select_by_value', 'select_by_visible_text']
可見 Select 物件提供了幾個好用的屬性與方法來操控選項 :
Select 物件屬性 | 說明 |
all_selected_options | 被選取之全部選項值 |
is_mutable | 傳回 True : select 元素為可複選 (multiple), 傳回 None : 單選 |
options | 傳回此 select 元素所有選項之 WebElement 物件串列 |
first_selected_option | 傳回此 select 元素被選取的第一個選項之 WebElement 物件 |
Select 物件方法 | 說明 |
select_by_index(index) | 選取索引 index 之選項 |
select_by_value(value) | 選取值為 value 之選項 |
select_by_visible_text(text) | 選取顯示文字為 text 之選項 |
deselect_by_index(index) | 取消選取索引 index 之選項 |
deselect_by_value(value) | 取消選取值為 value 之選項 |
deselect_by_visible_text(text) | 取消選取顯示文字為 text 之選項 |
deselect_all() | 取消所有已選取之選項 |
首先來檢視上面兩個 select 元素之 options 屬性 :
>>> len(idol_select.options) # 最受歡迎韓星有 5 個選項
5
>>> len(drama_select.options) # 推薦的韓劇有 4 個選項
4
這些選項都是 option 元素之 WebElement 物件 :
>>> idol_select.options[0]
<selenium.webdriver.remote.webelement.WebElement (session="0e04ca9b-fcdc-448d-b98e-f78d0b1a6e7e", element="4ec97417-4780-42c0-9b51-d084b52aab21")>
>>> drama_select.options[0]
<selenium.webdriver.remote.webelement.WebElement (session="0e04ca9b-fcdc-448d-b98e-f78d0b1a6e7e", element="4ae6eeb4-c03c-4564-aa99-b358010f1cee")>
>>> idol_select.options[0].text # 選項的顯示文字
'金智媛'
>>> drama_select.options[0].text # 選項的顯示文字
'揹著善宰跑'
檢視 is_multiple 屬性 :
>>> idol_select.is_multiple # 傳回 None : 單選
>>> drama_select.is_multiple # 傳回 True : 複選
True
呼叫 select_by_index(), select_by_value(), 與 select_by_visible_text() 來勾選可複選清單 drama_select 中的 "揹著善宰跑", "淚之女王" 與 "低谷醫生" :
>>> drama_select.select_by_index(0) # 選取索引 0 之 "揹著善宰跑"
>>> drama_select.select_by_value('淚之女王') # 選取 value 屬性為 "淚之女王"
>>> drama_select.select_by_visible_text('低谷醫生') # 選取顯示文字為 "低谷醫生"
這樣測試網頁上的這三個選項就被選取了 (模仿真人按 CTRL + 滑鼠點選) :
檢查 all_selected_options 屬性長度可知有 3 個選項已被選取, 用 text 屬性值確認它們都是上面三個指令所選取之選項 :
>>> len(drama_select.all_selected_options)
3
>>> drama_select.all_selected_options[0].text
'揹著善宰跑'
>>> drama_select.all_selected_options[1].text
'淚之女王'
>>> drama_select.all_selected_options[2].text
'低谷醫生'
>>> drama_select.first_selected_option.text # 被選取的第一個選項
'揹著善宰跑'
取消選取可以呼叫 deselect_by_xxx() 方法, 例如呼叫 deselect_by_value('淚之女王'), 這會使被選取之選項剩下兩個 :
>>> drama_select.deselect_by_value('淚之女王')
>>> len(drama_select.all_selected_options)
2
也可以呼叫 deselect_all() 把剩下的兩個被選取選項取消 :
>>> drama_select.deselect_all()
>>> len(drama_select.all_selected_options)
0
以上是 Select 物件的全部用法, 可見不管是單選或複選的 select 元素, 其 option 元素只提供了一個 click() 方法來做選取操作, 卻沒有取消選取操作, 功能很陽春, 而 Select 物件則提供了完整的方法來操作 select 元素.
上面的範例程式可以用 Select 類別改寫, 完整原始碼如下 :
# selenium_select_option_2.py
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.select import Select
import time
options=Options()
ua='Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0'
options.add_argument(f'user-agent={ua}')
driver=webdriver.Firefox(options=options)
driver.implicitly_wait(20)
url='https://tony1966.github.io/test/python/web_crawler/crawler_test_4.htm'
driver.get(url)
idol=driver.find_element('id', 'idol')
idol_select=Select(idol)
idol_select.select_by_visible_text('金智媛')
drama=driver.find_element('id', 'drama')
drama_select=Select(drama)
drama_select.select_by_index(1)
drama_select.select_by_value('寄生獸')
ok_btn=driver.find_element('id', 'ok')
ok_btn.click()
time.sleep(5)
driver.close()
執行結果與上面範例相同.
3. 執行 Javascript 與切換視窗 :
在上一篇測試中, 我們檢視了 WebDriver 物件有一個 execute_script() 方法可執行 Javascript 程式碼 (字串), 也擁有 current_window_handle 屬性可取得目前視窗的參考, windlw_handles 屬性則可取得所有 Selenium 開啟之瀏覽器視窗的參考, 以及 switch_to.window() 方法可切換至指定的視窗, 以下程式碼同時測試這幾個屬性與方法.
首先開啟瀏覽器載入 Google 首頁 :
>>> from selenium import webdriver
>>> driver=webdriver.Firefox()
>>> driver.implicitly_wait(20)
>>> url='https://www.google.com.tw'
>>> driver.get(url)
這時用 driver 的 window_handles 屬性取得的 Selenium 開啟之瀏覽器視窗參考串列應該只有一個元素, handles[0] 便是其參考, 也會等於 current_window_handle 屬性值 :
>>> handles=driver.window_handles
>>> len(handles)
1
>>> handles[0]
'f4b22ef6-c73b-4e7b-b4bb-cce358f28aa9'
>>> driver.current_window_handle
'f4b22ef6-c73b-4e7b-b4bb-cce358f28aa9'
接著定義一個 Javascript 程式碼字串用來開啟 Yahoo 首頁 :
>>> js='window.open("https://tw.yahoo.com");'
>>> driver.execute_script(js) # 執行 Javascript
這時會發現原先開啟的 Google 首頁已被 Javascript 開啟的 Yahoo 首頁取代, 檢視 window_handles 屬性會發現串列紀錄了兩個參考, current_window_handle 會指向第二個參考 (Yahoo) :
>>> handles=driver.window_handles
>>> handles
['f4b22ef6-c73b-4e7b-b4bb-cce358f28aa9', 'ffce75be-dc2e-4018-8694-9b07d1ce5fdc']
>>> driver.current_window_handle
'ffce75be-dc2e-4018-8694-9b07d1ce5fdc'
如果呼叫 switch_to.window() 並傳入第一個參考 (Google) 則會切回 Google 首頁 :
>>> driver.switch_to.window(handles[1])
>>> driver.current_window_handle
'ffce75be-dc2e-4018-8694-9b07d1ce5fdc'
總之切到哪一個參考就會載入哪個網頁, 並同時更新 current_window_handle 屬性值.
如果要在新分頁開啟網頁, 則 window.open() 要傳入第二參數 "_blank" :
>>> js='window.open("https://www.msn.com/zh-tw", "_blank");'
>>> driver.execute_script(js)
>>> driver.window_handles
['f4b22ef6-c73b-4e7b-b4bb-cce358f28aa9', 'ffce75be-dc2e-4018-8694-9b07d1ce5fdc', '76f0cc6e-b05d-4ff4-90f5-3b4a924b40d7']
可見新增了一個視窗參考.
參考 :
沒有留言:
張貼留言