--- title: 'Python Selenium 教學筆記' disqus: hackmd --- Python Selenium with VSCODE 教學筆記 === 綱要 [TOC] :::info 不知道為什麼這篇莫名其妙在Google搜尋「Selenium 教學」的排名特別前面... 來信問開課的人突然暴增,但目前沒有收費開課的打算,所以之後會找個時間把進階教學寫一寫,有更多對E2E自動化測試有興趣的人一起學習是好事。 ::: 關於Chrome的Webdriver === :::info 如果你已經使用了115版以後的版本,要改從這裡下載相應的Driver https://googlechromelabs.github.io/chrome-for-testing/#stable ::: 基本環境安裝 === Selenium自動化測試需要借用能操作瀏覽器的Driver之外,也需要輔以一些額外的套件進行。 以下依照順序詳解: ## Windows ### 一、安裝Python最新版 安裝最新版本的Python(目前3.9.6):https://www.python.org/downloads/ 安裝路徑預設在windows上是: `C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39` ![](https://i.imgur.com/tPDBvuG.png) 記得在安裝畫面將`Add Python 3.9 to PATH`也勾選起來,便於之後安裝一些Package。 安裝好後如下所示,以「系統管理者身分」執行cmd ![](https://i.imgur.com/cAaOv1E.png) 接著在終端機上輸入`python`,看到這訊息代表Python安裝成功了。 ![](https://i.imgur.com/7da4WBS.png) 由於部分使用者在執行Python指令時,WINDOWS會彈出線上商店的關係。 現在需要到WINDOWS的系統環境變數修改些路徑: ![](https://i.imgur.com/1S5Y6MD.png) 按下「編輯系統環境變數」後會看到這個畫面,再按「環境變數」 ![](https://i.imgur.com/L97FDmk.png) 進入環境變數視窗後選擇「path」>> 「編輯」,檢查看看Python的安裝路徑。 確認Python是不是擺在「windowApps」之前。 如果不是,就用右邊的上移、下移按鈕處理後按下確定。 ![](https://i.imgur.com/g7lGX5V.png) 假如你安裝Python時忘了把Add to path打勾 那就把這個路徑新增到系統變數 >> Path裡面去: `C:\Users\你的電腦帳號\AppData\Local\Programs\Python\Python目前版本\Scripts` ![](https://i.imgur.com/8CYDhEO.png) **權限問題排除** :::info 若敲指令時發生「python 不是内部或外部命令」的錯誤訊息出現時。 我們需要調整一下Windows的PowerShell權限設定。 ::: 在開始選單下的「Windows PowerShell」下,以系統管理員身分執行一次 ![](https://i.imgur.com/uhOFSv9.png) 並且輸入指令:`Set-ExecutionPolicy RemoteSigned` ![](https://i.imgur.com/RrF9a6p.png) 輸入A選擇「全部皆是」,然後把VSCODE和終端機重開即可。 ![](https://i.imgur.com/Pw9Lfhx.png) ### 二、安裝Selenium套件 然後繼續輸入指令`quit()`跳出剛才有`>>>`的Python指令區 現在輸入我們要安裝Selenium的指令 `pip install selenium` 出現`Successfully installed selenium` 的訊息即可。 ![](https://i.imgur.com/ccuHtYz.png) --- ### 三、安裝 WebDriver 自動測試需要使用到瀏覽器行為的控制事件,所以我們要安裝相應的WebDriver以及版本。 如果你不清楚現在使用的Chrome版本,網址輸入`chrome://settings/help` 可以查看: ![](https://i.imgur.com/1qXplqR.png) **下載 Chrome WebDriver** https://chromedriver.chromium.org/downloads :::info 如果有遇到Chrome更新,就要重新安裝對應版本的WebDriver ::: WebDriver存放的路徑,建議是放在Python安裝好的根目錄下,也就是: `C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39` 這樣即可在任何路徑下的腳本中隨時調用driver的功能。 第二個方式比較不推薦,不過當有著需要測試不同版本瀏覽器時可以參考看看: ``` regression_test ├── case_1 │ ├── chromedriver.exe │ └── test_login.py ├── case_2 │ ├── chromedriver.exe │ └── test_keno_bet.py ``` 由於通常測試腳本都會進入Git版控管理,Driver本身的體積就有10MB左右 這樣做反而會讓專案打包Push上去遠端後佔用太多容量。 --- ### 四、安裝BeautifulReport自動測試報告套件 ![](https://i.imgur.com/q9ExpBM.png) **下載並安裝BeautifulReport** 假如你已經會使用Git,直接Clone這個位置: `https://github.com/TesterlifeRaymond/BeautifulReport.git` ~~或者前往Github網址: (作者不再更新,然後原始版本的cdn異常緩慢) ~~https://github.com/TesterlifeRaymond/BeautifulReport~~ 建議試著用我改好cdn位置的繁體化修改版本: https://drive.google.com/file/d/1zQlAt1p-0CQoXnIjJFLdFp7SCuuVkZG2/view?usp=sharing 按下Code後選擇**Download ZIP** ![](https://i.imgur.com/AyTdFXE.png) 解壓縮後將目錄名稱改為BeautifulReport ![](https://i.imgur.com/UIBlzN4.png) 點入BeautifulReport會看到這些檔案: ![](https://i.imgur.com/0UOUWt7.png) 回上一層,將整個BeautifulReport目錄複製到以下路徑即可: `C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39\Lib\site-packages\` **安裝webdriver-manager** 接著回到終端機,我們需要webdriver manager的支援。 輸入指令: `pip install webdriver-manager` --- ### 五、安裝VSCODE與IDE擴充套件 ![](https://i.imgur.com/h4obXNA.png) 網址:https://code.visualstudio.com/ 安裝成功後打開VSCODE,按一下左下的**Extensions**開啟擴充套件視窗,搜尋以下幾個套件: **1. Pylance** ![](https://i.imgur.com/zQFsLEl.png) 微軟提供的語法智能輔助工具,包含語法檢查與Debugging。 **2. Path Intellisense** ![](https://i.imgur.com/VwZdqjj.png) 路徑選擇的輔助工具,當需要輸入來源程式碼、圖片、CSS等檔案時 依照輸入 `../` 或 `./` 的不同,自動會幫我們開啟相關目錄下拉視窗來選擇來源檔案。 --- ## Mac OSX ### 安裝Home Brew(有裝過的話可以跳過) ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` ### 使用Home Brew安裝Python 3 ```bash brew install python3 ``` ### 安裝pip 3套件管理工具 ```bash python3 get-pip.py ``` 確認有正確安裝pip 3 ```bash pip3 --version ``` ### 安裝Selenium 4.0 ```bash pip3 install selenium ``` ### 安裝webdriver manager套件 ```bash pip3 install webdriver-manager ``` ### 下載webdriver https://chromedriver.chromium.org/downloads 下載好相應版本後放在mac根目錄下: ==**/usr/local/bin**== 如果你的chrome版本超過114,請下載這裡的 https://googlechromelabs.github.io/chrome-for-testing/ --- ### 將BeautifulReport套件下載後解壓縮到以下路徑 下載位置:https://drive.google.com/file/d/1zQlAt1p-0CQoXnIjJFLdFp7SCuuVkZG2/view?usp=sharing 解壓縮到: ```bash /opt/homebrew/lib/python3.11/site-package ``` --- ### VSCODE設定 和Windows沒有相異,所以VSCODE的設定參照一下Windows的部分即可 --- # 腳本對於元素定位的XPATH是怎麼找出來的 Python Selenium對於取得當前需要被操作的元素,常見方法有: **1. XPATH** HTML經過編譯成XML後找出的對應位置,分絕對位置與相對位置。 相對位置:`//div[@class='ytp-tooltip-text-wrapper']` 絕對位置: `/html[1]/body[1]/ytd-app[1]/div[1]/ytd-page-manager[1]/ytd-watch-flexy[1]/div[5]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/ytd-player[1]/div[1]/div[1]/div[10]/div[1]` **2. CSS Selector** 可以透過CSS選取器取得元素定位。 **3. ID** HTML上某元素指定的id="ooo" **4. ClassName** HTML上某元素含有的class="aaa bbb ccc" 先來安裝尋找XPATH的Chrome小工具: **ChroPath** **ChroPath** ![](https://i.imgur.com/w6PujRt.png) 第二個工具是**ChroPath**,同樣加入擴充套件並將它釘選起來 ![](https://i.imgur.com/JHa9Hzx.png) https://chrome.google.com/webstore/detail/chropath/ljngjbnaijcbncmcnjfhigebomdlkcjo?hl=zh-TW 重新啟動瀏覽器後,在網頁上開啟開發人員工具(f12) ![](https://i.imgur.com/PeB3Xhl.png) 在**Elements**頁籤下,中間會有Styles、Computed等的選單,按下「>>」即可找到**ChroPath** ![](https://i.imgur.com/wW6Dp2n.jpg) 接著網頁上無論是透過開發人員工具或者滑鼠右鍵 >> 檢查,都可以找到該元素在**ChroPath**上的Rel XPATH(Relative相對位置)以及Abs XPATH(Absolute絕對位置)。 ![](https://i.imgur.com/6ceO6D4.png) 這能補足**RUTO**找不到完整XPATH時的缺陷 --- # 開始撰寫第一個測試腳本 ==**測試目標:打開Youtube搜尋hololive的虛擬主播節目**== **1. 建立新檔案** 在D槽建立一個目錄,命名為`auto_test` 打開VSCODE,選取左上檔案圖示後點選Open Folder ![](https://i.imgur.com/6VjC82M.png) 在左側的檔案列表最上面,按下新增圖示,並將檔名命名為`firstCase.py` ![](https://i.imgur.com/QM1gj7g.png) **2. 引入常用WebDriver API** ```python= from selenium import webdriver ## BY: 也就是依照條件尋找元素中XPATH、CLASS NAME、ID、CSS選擇器等都會用到的Library from selenium.webdriver.common.by import By ## keys: 鍵盤相關的Library from selenium.webdriver.common.keys import Keys ## Select: 下拉選單相關支援,但前端框架UI工具不適用(ex: Quasar、ElementUI、Bootstrap) from selenium.webdriver.support.ui import Select ## WebDriverWait: 等待頁面加載完成的顯性等待機制Library from selenium.webdriver.support.ui import WebDriverWait ## ActionChains: 滑鼠事件相關 from selenium.webdriver.common.action_chains import ActionChains ## expected_conditions: 條件相關 from selenium.webdriver.support import expected_conditions as EC ## BeautifulReport: 產生自動測試報告套件 from BeautifulReport import BeautifulReport ## Chrome WebDriver 需要DRIVER Manager的支援 from webdriver_manager.chrome import ChromeDriverManager ## 延遲時間相關 import time ## 單元測試模組,線性測試用不到 import unittest ``` **ActionChain API 內容** :::info 點擊滑鼠右鍵: context_click() 雙擊滑鼠左鍵: double_click() 按著滑鼠左鍵不放: click_and_hold() 放開滑鼠左鍵: release() 拖曳到某個元素後放開: drag_and_drop(source, target) 拖曳到某個座標後放開: drag_and_drop_by_offset(source, xoffset, yoffset) 按下鍵盤上某個按鍵: key_down(value) 放開鍵盤上某個按鍵: key_up(value) 滑鼠指標從當前位置移動到某個座標: move_by_offset(xoffset, yoffset) 滑鼠指標移動到某個元素: move_to_element(to_element) 移動到某元素附近座標位置: move_to_element_with_offset(to_element, xoffset, yoffset) 執行當前這個ActionChain的動作: perform() 在元素上輸入值(ex:input): send_keys(value) 在指定的元素上輸入值: send_keys_to_element(element, value) ::: ```python= ##剛才IMPORT的部分(省略)### ### ## 設定Chrome的瀏覽器彈出時遵照的規則 ## 這串設定是防止瀏覽器上頭顯示「Chrome正受自動控制」 options = webdriver.ChromeOptions() options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) ## 關閉自動記住密碼的提示彈窗 options.add_experimental_option("prefs", { "profile.password_manager_enabled": False, "credentials_enable_service": False}) ``` 接下來是測試腳本的內容,我們要將腳本定義完成這些動作需要幾個Case。 - 1. 前往Youtube網站後,搜尋Gura的影片,關鍵字「Gawr Gura 熟肉」 - 2. 在搜尋結果找到特定的影片並點進去 - 3. 回上頁列表,重新整理網頁後,切換另一則影片,五秒後回到首頁 ```python= ## 剛才import進來的東西與基本設定,省略 #### ## 我們如果要將CASE拆成幾個不同的方法,需要用一個Unitest Class包覆起來 ## 然後加上修飾符@classmethod class Test(unittest.TestCase): @classmethod ## setUpClass這邊的設定是,可以讓所有Case進行過程中只開啟一次瀏覽器 ## 執行時會依照這個順序循環一次 setUpClass > test > teardown ## self則是作為我們的區域參數來定義作用域 def setUpClass(self): ## 定義WebDriver以及ActionsChain的變數便於後頭應用 self.driver = webdriver.Chrome(chrome_options=options) self.action = ActionChains(self.driver) ## 開啟Chrome新視窗,前往Youtube網址並最大化視窗 self.URL = "https://youtube.com" self.driver.get(self.URL) self.driver.maximize_window() @classmethod def tearDownClass(self): ## 所有case跑完後就退出瀏覽器 self.driver.quit() ## Test Case 的命名方式務必以「test_01_* ~ test_99_*」為主,讓爬蟲依照順序走 ## """裡面的註解就是報表產生後的CASE描述文字。 ## time.sleep(number)數字代表一秒 def test_01_search(self): """ 前往Youtube網站後,搜尋Gura的影片 """ time.sleep(2) ## 等待頁面中的HTML,ID = 'search'這個元素出現後才執行動作 try: element = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.XPATH, "//input[@id='search']")) ) finally: time.sleep(2) ## 找到搜尋框的XPATH並且輸入'Gawr Gura 熟肉' 熟肉就是經過別人得到版權方同意,翻譯上傳過後的剪輯 ## 寫法上也通driver.find_element(By.XPATH, '//xxx') input_search = self.driver.find_element_by_xpath("//input[@id='search']") input_search.send_keys("Gawr Gura 熟肉") ## 找到搜尋按鈕後按下 button_search = self.driver.find_element_by_id("search-icon-legacy") button_search.click() def test_02_open_target(self): """ 在搜尋結果找到特定的影片並點進去 """ time.sleep(5) ## 滾動網頁往下拉 self.driver.execute_script("window.scrollBy(0,1200)") time.sleep(2) ## 隨便找一個影片的連結,利用XPATH找路徑 youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=9SfsF_6fY9c']") youtube_target.click() time.sleep(5) def test_03_back_to_list(self): """ 回上頁列表,重新整理網頁後,切換另一則影片,五秒後回到首頁 """ self.driver.back() time.sleep(2) self.driver.refresh() time.sleep(3) ## 注意,youtube可能因為你所在位置而針對搜尋結果作排序 ## 如果你發現突然跳出case導致失敗,很有可能是因為搜尋列表找不到影片 ## 將影片的 /watch?v=xxxxx替換成該搜尋結果中的任一連結即可 youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=MhVh01k_Wg0']") youtube_target.click() time.sleep(5) logo = self.driver.find_element_by_id("logo-icon") logo.click() time.sleep(5) ``` 程式碼的尾端我們在這將產生測試報告的設定做好: ```python= ## 剛剛寫腳本的區域,省略 ### # basedir就是存放所有TEST Case的目錄,讓它爬 pattern = '*.py',所以要做哪個類別的測試就指定哪個前贅 # 路徑盡量放D槽喔,除非你的電腦只有C basedir = "D:/auto_test/" if __name__ == '__main__': # 取得資料夾目錄底下,符合後面任何副檔名為.py,並進行所有test的測試項目 test_suite = unittest.defaultTestLoader.discover( basedir, pattern='*.py') # 測試結果加入到 BeautifulReport 套件內 result = BeautifulReport(test_suite) # 結果產生Report 檔案名稱為 filename, 敘述為 description, log_path 預設放在跟目錄底下就行 result.report(filename='report', description='我的第一個測試', log_path='D:/auto_test/') # 啟動自動化指令,在終端機輸入: & C:/Users/你的使用者帳號/AppData/Local/Programs/Python/Python39/python.exe d:/auto_test/firstCase.py ``` # 完整CODE範例 跑自動測試的終端機啟動指令: `& C:/Users/你的使用者名稱/AppData/Local/Programs/Python/Python39/python.exe d:/auto_test/firstCase.py` 執行自動化測試的這段時間你可以選擇從頭看到尾,也可以丟著讓他跑完等報告就好。 ```python= from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support import expected_conditions as EC from BeautifulReport import BeautifulReport from webdriver_manager.chrome import ChromeDriverManager import time import unittest options = webdriver.ChromeOptions() options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) options.add_experimental_option("prefs", { "profile.password_manager_enabled": False, "credentials_enable_service": False }) class Test(unittest.TestCase): @classmethod def setUpClass(self): self.driver = webdriver.Chrome(chrome_options=options) self.action = ActionChains(self.driver) self.URL = "https://youtube.com" self.driver.get(self.URL) self.driver.maximize_window() @classmethod def tearDownClass(self): self.driver.quit() def test_01_search(self): """ 前往Youtube網站後,搜尋Gura的影片 """ time.sleep(2) try: element = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.XPATH, "//input[@id='search']")) ) finally: time.sleep(2) input_search = self.driver.find_element_by_xpath("//input[@id='search']") input_search.send_keys("Gawr Gura 熟肉") button_search = self.driver.find_element_by_id("search-icon-legacy") button_search.click() def test_02_open_target(self): """ 在搜尋結果找到特定的影片並點進去 """ time.sleep(5) self.driver.execute_script("window.scrollBy(0,1200)") time.sleep(2) youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=9SfsF_6fY9c']") youtube_target.click() time.sleep(5) def test_03_back_to_list(self): """ 回上頁列表,重新整理網頁後,切換另一則影片,五秒後回到首頁 """ self.driver.back() time.sleep(2) self.driver.refresh() time.sleep(3) youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=MhVh01k_Wg0']") youtube_target.click() time.sleep(5) logo = self.driver.find_element_by_id("logo-icon") logo.click() time.sleep(5) basedir = "D:/auto_test/" if __name__ == '__main__': test_suite = unittest.defaultTestLoader.discover(basedir, pattern='*.py') result = BeautifulReport(test_suite) result.report(filename='report',description='我的第一個測試', log_path='D:/auto_test/') ``` :::info 如果你有檢查資料正確與否的需求 def 方法下面可以加上 assert 判別結果是否符合測試例子 ::: ```python= def test_01_new_test(self): """ 斷言例子 """ test_val = "亂叫的小鯊魚" ## ...一些事件後 target_text = self.driver.find_element_by_xpath("任何你找到的位置").text ## assert 就是在驗證結果的布林值,沒事不建議直接讓他 = true/false assert target_text = test_val time.sleep(5) ``` 順利的話報告會產生在D:/auto_test下,名稱是report.html 可以打開來看看,到這步驟就結束囉! ![](https://i.imgur.com/6dLqnbH.png) ###### tags: `Python` `Selenium`