簡單來說,Selenium 可以用程式碼來模擬一個人在瀏覽器(Chrome, Safari, Firefox, …)中的動作。一般來說,Selenium 通常被作為自動化測試(Automated Testing)的工具,但由於他可以模擬在瀏覽器中的操作,所以也可以作為讓我們可以自動化某些操作的輔助工具。
在作為輔助工具上,Selenium 和 PyAutoGui 不同的地方為:在滑鼠方面,PyAutoGui 是使用「座標定位」的方式去進行常見的操作(click, doubleclick, …),而 Selenium 是使用選取「網頁元素(element)」的方式去操作。
這篇教學將以 Python 程式來自動化模擬網頁操作為主軸。
在使用 Selenium 之前,我們需要先了解「網頁元素(element)」到底是怎麼一回事。
這是一段簡單的 html 代碼:
在 HTML 裡面,我們會使用許多標籤(tag) 來描述網頁的內容,如 p
(段落)、strong
(粗體字)、a
(超連結),並可以在標籤內加上其屬性(Attribute),如 class="description"
(CSS class)、href="https://www.nctu.edu.tw"
(設置超連結網址),而標籤會有開始標籤(start tag)與結束標籤(end tag),在其之間可填入內容,接著這一整串變形成了一個元素(element)。
p
、strong
、a
class="description"
、href="https://www.nctu.edu.tw"
<a href="https://www.nctu.edu.tw">交大首頁請點我</a>
、<strong>每週四下午</strong>
、上方整段 code。不同的 HTML 標籤分別代表不同的意涵,如 p
代表段落、a
代表超連結、img
代表圖片、strong
代表粗體文字。為了讓網頁更好看,人們想要透過 CSS(Cascading Style Sheets,描述 HTML 的樣式) 去詳細設定網頁中每一處的顯示樣式為何。
通常我們會建立另一個 .css 檔案去撰寫樣式,而此時,我們就需要指定特定樣式要在哪個元素上生效。我們稱之為 CSS Selector (CSS 選擇器) ,替我們指定 HTML 中的特定元素。而 CSS Selector 可簡單分為以下三種:
id="xxxx"
的元素上。每個網頁只應該出現一次(identification 的概念)。在 CSS 中寫作 #xxxx。class="xxxx"
的元素上。每個網頁可以出現很多次。在 CSS 中寫作 .xxxx。p
、strong
等,在 CSS 直接寫 tag name。如果以上方的 HTML code 來說,假設我想要讓整個 <p class="description">...</p>
段落的字為藍色,我可以撰寫以下的 CSS:
如果我們寫
則所有使用 p 的元素的字都會是藍色。
此外,CSS Selector 還可以有階層的方式一層一層指定下去:
或是一次指定很多個東西:
還有很多詳細的用法,想了解的同學可以瀏覽:W3School CSS 教程 與 W3School HTML 教程。
在接下來的教學中,我們會需要寫一些自動化的程式來模擬網頁上的操作。首先,有了 CSS Selector 的概念後,我們若可以知道網頁上某些按鈕、連結、文字、照片等元素的 CSS Selector,就可以把這些 Selector 提供給程式,讓程式來定位網頁中的元素並對其進行操作。
在 Chrome 中,假設我們要知道交大首頁的「在校生」連結的元素的 Selector 為何,首先我們先在連結上點右鍵,並點選「檢查」。
然後在反白的那行上面,右鍵點選 Copy > Copy selector:
我們可以拿到一串針對「在校生」這一個連結的 Selector:
之後我們可以把這個用在 Selenium 中的選擇網頁元素的部分。
首先,我們需要將 selenium 給引入,而其中的 selenium.webdriver 模組會涵蓋我們會使用到的指令。另外也引入了 time.sleep 模組,稍後會用到。
再來,下面第一行可以打開 Chrome,而第二行 driver.get(網址)
則是告訴 webdriver 要連到哪一個網址去。
使用 driver.title
可以取得當前網頁標題,並使用 assert 來確定我們連到的網頁標題叫做 NCTU 國立交通大學
。
driver.find_element_by_css_selector
可以用來定位特定 CSS Selector,在此我們使用前面拿到的「在校生」連結的 Selector,當作參數傳進函式。此時 student 即為「在校生」那一個連結元素。
我們可以寫 student.click()
來點擊該元素。
最後,為了看到我們真的有連到「在校生」的那一頁,讓程式暫停五秒。最後再執行 driver.close()
來關閉 webdriver,程式結束。
注:同 Getting Started。
這段程式碼會到 python.org 的網站上,在搜尋框中鍵入 array,並看看搜尋結果頁有沒有結果。
首先,我們多載入一個 Keys,讓我們之後可以對一個輸入框按鍵盤的特定按鍵(如 Return, Ctrl, Shift 等等,稍後會用到的是 Return 鍵)
改成進入 python.org。
從 Chrome 檢查元素面板中,可以發現那一個搜尋框有著 name="q"
的屬性。Selenium 同時也支援用 name
屬性來尋找元素,所以我們可以寫
driver.find_element_by_name("q")
。
接著,elem.clear()
可以保證搜尋框會是乾淨的。接著我們使用 elem.send_keys("array")
,代表在 elem 上按下鍵盤的按鍵 array
。最後再按下一個 Return 鍵,用 Keys.RETURN
表示。
詳細的 Keys 用法可參考:module-selenium.webdriver.common.keys
name="xxxx" 是網頁表單會使用到的一個屬性,用來指定該輸入框資料的名稱(name)。
上面的範例中我們分別獲取了「在校生」連結元素與 Python.org 的搜尋輸入框元素,前者使用 CSS Selector 的方式(find_element_by_css_selector),後者使用 name 屬性(find_element_by_name)。以下方法的回傳型態是 WebElement 。
Selenium 還提供了其他選擇網頁元素的方法,如下:
<a href="xxx">這是連結文字</a>
連結元素與文字,則可以使用 find_element_by_link_text("這是連結文字")
來定位之。<a href="xxx">這是連結文字</a>
連結元素與文字,則可以使用 find_element_by_partial_link_text("文字")
來部分匹配並定位之。以上的方法會回傳找到的第一個元素,如果想要支援回傳所有符合條件的元素,可以使用以下方法(element 加 s),會回傳一個 list:
通常我們會把找到的元素指派(assign)給一個變數,如
此時我們就可以對這個元素(如 elem)進行點擊與按下鍵盤按鍵:
如果要支援更多操作,如雙擊、拖拉、按住按鍵不放等,我們需要使用 Action Chains。
API 文件:Action Chains
在使用 Action Chains 前,可以載入 ActionChains class 讓程式更簡單一點:
接著,Action Chains 支援絕大多數在網頁中會有操作,如下:
使用時,只要先建立一個新的 Action Chains,之後就可以開始執行你要的操作。下面的範例是按下 Ctrl + c。
也可以寫成這樣:
在網頁中,我們時常會遇到點擊一個按鈕或連結後,就跳出一個新的視窗(window)。Selenium 也可以幫我們處理這類型的操作。
driver.window_handles
:取得所有的視窗(回傳一個 list)driver.current_window_handle
:取得當前視窗(回傳一個 string)driver.switch_to.window(window_name)
:切換至 window_name。範例程式:先打開交大網頁的網站導覽頁,再點擊校園公告頁,並切換到校園公告頁。
會輸出:
在 HTML 中,有一類的元素我們必須特別處理:頁框(frame)。頁框白話地說,就是在一個網頁中,再載入另外一個網頁。常見的範例是,很多網站都會使用頁框的方式,載入 facebook 粉絲團的按讚框。
在 HTML 中,頁框包含兩種標籤:frame
與 iframe
。
由於頁框會載入另一個網頁,所以程式沒有辦法直接存取頁框裡的元素。我們必須切換到頁框內,才能夠取得頁框裡頭的元素,但切換之後僅能取得頁框內的元素,原本網頁的元素則會變得無法存取。關於操作頁框,可以使用:
driver.switch_to.frame(another_frame)
:切換到 another_frame。another_frame 可以接受以下幾種形式:
driver.switch_to.parent_frame()
:切換到父頁框,若已是主網頁,則無效果driver.switch_to.default_content()
:切換到主網頁舉例來說,若今天有兩個網頁 a.html 與 b.html,內容分別是:
此時,如有以下的程式:
我們就可以進行對頁框的操作。
若一個頁框內還有另一個頁框,則需再切換一次。
更詳細的教學文章:Python selenium —— 深刻解析及操作frame、iframe
由於打開新的視窗和頁框都需要花一些時間才能夠完全載入,為了確保我們每次尋找元素、切換視窗、切換頁框都有確實地被正確執行,我們會需要適當的時機做延遲(delay)的動作。
Selenium 內建有 WebDriverWait 的函式,不過為了簡單起見,也可以直接使用 time.sleep 來暫停若干秒,等待載入完成。詳細可參考接下來的範例程式。
這個程式可以幫助你在一個課程的檔案相當多時,直接用程式將所有檔案下載回來。要特別注意的是,E3 點擊 [ view ] 之後出現的 popup window 裡面都含有一個頁框(frame),所以必須要切換進頁框後,再對頁框內的元素進行操作。此外,還需要等待視窗與頁框被完全載入後,才能操作元素,以免發生問題。