2024年6月7日 星期五

Python 學習筆記 : Selenium 4 用法 (中)

今天繼續整理 Selenium 4 的用法, 本系列之前的文章參考 :



五. 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']

可見新增了一個視窗參考. 

參考 :


沒有留言:

張貼留言