# Python Selenium 筆記 ###### tags: `Python` ## 參考資料 * [Selenium-Python中文文档](https://selenium-python-zh.readthedocs.io/en/latest/getting-started.html) * [Python selenium —— 深刻解析及操作frame、iframe](https://huilansame.github.io/huilansame.github.io/archivers/switch-to-frame) ## 安裝Selenium 指令如下: ``` pip3 install selenium ``` ## 下載與安裝 Chrome Driver 請前往下方連結,下載對應版本的 Chrome Driver,並將壓縮檔內的**chromedriver.exe**解壓縮到指定的工作目錄。 [ChromeDriver - WebDriver for Chrome](http://chromedriver.chromium.org/downloads) ## 使用 Selenium 開啟 Chrome 瀏覽器 開啟 Chrome 瀏覽器,並前往 Google 搜尋頁面,然後再關閉瀏覽器,程式碼如下: ```python= from selenium import webdriver driver = webdriver.Chrome('chromedriver.exe') driver.get('https://www.google.com') driver.close() ``` ## 使用 Selenium 查找元素定位 ### 通過 Class name 定位元素 當你通過 class name 查找元素時可使用這個。在該策略下,頁面中第一個匹配該 class 屬性的元素會被匹配並返回。如果找不到任何元素,則會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_class_name('class name') ``` ### 通過CSS選擇器查找元素 當你通過CSS選擇器查找元素時可以使用這個。在該策略下,頁面中第一個匹配該 CSS 選擇器的元素會被匹配並返回。如果找不到任何元素,則會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_css_selector('css selector') ``` ### 通過 ID 查找元素 當你知道一個元素的 id 時,你可以使用本方法。在該策略下,頁面中第一個符合該 id 的元素會被匹配並返回。如果找不到任何元素,會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_id('id') ``` ### 通過連結文字查找元素 當你知道在一個連結標籤中使用的文字時可使用這個。在該策略下,頁面中第一個匹配連結文字的標籤會被匹配並返回。如果找不到任何元素,則會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_link_text('link text') ``` 也可以只用連結的部分文字定位元素: ```python= driver.find_element_by_partial_link_text('partial link text') ``` ### 通過 Name 查找元素 當你知道一個元素的 name 時,你可以使用本方法。在該策略下,頁面中第一個該 name 元素會被匹配並返回。如果找不到任何元素,會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_name('name') ``` ### 通過標籤名 tag 查找元素 當你通過標籤名 tag 查找元素時可使用這個。在該策略下,頁面中第一個匹配該標籤名的元素 會被匹配並返回。如果找不到任何元素,會拋出 NoSuchElementException 異常。 ```python= driver.find_element_by_tag_name('tag') ``` ### 通過XPath查找元素 XPath是XML文檔中查找結點的語法。因為HTML文檔也可以被轉換成XML(XHTML)文檔, Selenium的用戶可以利用這種強大的語言在web應用中查找元素。 XPath擴展了(當然也支持)這種通過id或name屬性獲取元素的簡單方式,同時也開闢了各種新的可能性, 例如獲取頁面上的第三個複選框。 使用XPath的主要原因之一就是當你想獲取一個既沒有id屬性也沒有name屬性的元素時, 你可以通過XPath使用元素的絕對位置來獲取他(這是不推薦的),或相對於有一個id或name屬性的元素(理論上的父元素)的來獲取你想要的元素。 XPath定位器也可以通過非id和name屬性查找元素。 絕對的XPath是所有元素都從根元素的位置(HTML)開始定位,只要應用中有輕微的調整,會就導致你的定位失敗。但是通過就近的包含id或者name屬性的元素出發定位你的元素,這樣相對關係就很靠譜, 因為這種位置關係很少改變,所以可以使你的測試更加強大。 ```python= driver.find_element_by_xpath('//*[@id="lst-ib"]') ``` ## 使用 Selenium 操作網頁元素 開啟 Chrome 瀏覽器,並前往 Google 搜尋頁面,然後搜尋"你好"並按下 Enter,程式碼如下: ```python= from selenium import webdriver driver = webdriver.Chrome('chromedriver.exe') driver.get('https://www.google.com') q = driver.find_element_by_name('q') q.send_keys('你好') from selenium.webdriver.common.keys import Keys q.send_keys(Keys.RETURN) ``` 開啟 Chrome 瀏覽器,並前往 Google 搜尋頁面,然後點擊登入按鈕,程式碼如下: ```python= from selenium import webdriver driver = webdriver.Chrome('chromedriver.exe') driver.get('https://www.google.com') login = driver.find_element_by_link_text('登入') login.click() ``` ## 使用 Selenium 登入Youtube 程式碼如下: ```python= from selenium import webdriver import time driver = webdriver.Chrome('chromedriver.exe') driver.get('https://www.youtube.com/') yt_login = driver.find_element_by_xpath('//*[@id="buttons"]/ytd-button-renderer/a') yt_login.click() # 輸入 email yt_email = driver.find_element_by_xpath('//*[@id="identifierId"]') yt_email.send_keys('email') # Google 帳號 email # 點擊繼續按鈕 yt_cte = driver.find_element_by_xpath('//*[@id="identifierNext"]/div/button') yt_cte.click() # 輸入密碼 yt_pwd = driver.find_element_by_xpath('//*[@id="password"]/div[1]/div/div[1]/input') yt_pwd.send_keys('password') # Google 帳號的密碼 # 點擊繼續按鈕 yt_cte = driver.find_element_by_xpath('//*[@id="passwordNext"]/div/button') yt_cte.click() # 前往指定的 Youtube 頁面 driver.get('Youtube Link') ``` ## 使用 Selenium 在Youtube 聊天室中留言 程式碼參考: [Python selenium —— 深刻解析及操作frame、iframe](https://huilansame.github.io/huilansame.github.io/archivers/switch-to-frame) 在留言欄位輸入文字 "1" 並送出,程式碼如下: ```python= driver.switch_to.frame('chatframe') driver.find_element_by_css_selector('div#input').send_keys('1') driver.find_element_by_css_selector('#send-button').click() ``` ## 使用 Selenium 操作 frame 標籤中的元素 很多人在用selenium定位頁面元素的時候會遇到定位不到的問題,明明元素就在那兒,就是定位不到,這種情況很有可能是**frame** 在搞鬼(可能原因之一),可以去仔細看看你網頁的DOM樹! **frame**標籤有**frameset**、**frame**、**iframe**三種,**frameset跟其他普通標籤沒有區別,不會影響到正常的定位**,而frame與iframe對selenium定位而言是一樣的,selenium有一組方法對frame進行操作: ```python= driver.switch_to.frame(reference) # 切到指定frame,可用id或name(str)、index(int)、元素(WebElement)定位 driver.switch_to.parent_frame() # 切到父级frame,如果已是主文档,则无效果 driver.switch_to.default_content() # 切到主文档,DOM树最开始的<html>标签 ``` ### 1. 怎麼切到frame中(switch_to.frame()) selenium提供了switch_to.frame()方法來切換frame ```python= switch_to.frame(reference) ``` **reference**是傳入的參數,用來定位frame,可以傳入id、name、index以及selenium的WebElement對象,假設有如下HTML代碼 index.html: ```htmlmixed= <html lang="en"> <head> <title>FrameTest</title> </head> <body> <iframe src="a.html" id="frame1" name="myframe"></iframe> </body> </html> ``` 想要定位其中的iframe並切進去,可以通過如下代碼: ```python= from selenium import webdriver driver = webdriver.Firefox() driver.switch_to.frame(0) # 1.用frame的index来定位,第一个是0 # driver.switch_to.frame("frame1") # 2.用id来定位 # driver.switch_to.frame("myframe") # 3.用name来定位 # driver.switch_to.frame(driver.find_element_by_tag_name("iframe")) # 4.用WebElement对象来定位 ``` 通常採用id和name就能夠解決絕大多數問題。但有時候frame並無這兩項屬性,則可以用index和WebElement來定位: * index從0開始,傳入整型參數即判定為用index定位,傳入str參數則判定為用id/name定位 * WebElement對象,即用find_element系列方法所取得的對象,我們可以用tag_name、xpath等來定位frame對象 舉個栗子: ```htmlmixed= <iframe src="myframetest.html" /> ``` 用xpath定位,傳入 WebElement 物件: ```python= driver.switch_to.frame(driver.find_element_by_xpath("//iframe[contains(@src,'myframe')]")) ``` ### 2. 從frame中切回主文檔(switch_to.default_content()) 切到frame中之後,我們便不能繼續操作主文檔的元素,這時如果想操作主文檔內容,則需切回主文檔。 ```python= driver.switch_to.default_content() ``` ### 3. 嵌套frame的操作(switch_to.parent_frame()) 有時候我們會遇到嵌套的frame,如下: ```htmlmixed= <html> <iframe id="frame1"> <iframe id="frame2" / > </iframe> </html> ``` 1. 從主文檔切到frame2,一層層切進去 ```python= driver.switch_to.frame("frame1") # 先切到frame1 driver.switch_to.frame("frame2") # 再切到frame2 ``` 2. 從frame2再切回frame1,這裡selenium給我們提供了一個方法能夠從子frame切回到父frame,而不用我們切回主文檔再切進來。 ```python= driver.switch_to.parent_frame() # 如果目前已是主文檔,則無效果 ``` 有了parent_frame()這個相當於後退的方法,我們可以隨意切換不同的frame,隨意的跳來跳去了。 所以只要善用以下三個方法,遇到frame分分鐘搞定: ```python= driver.switch_to.frame(reference) driver.switch_to.parent_frame() driver.switch_to.default_content() ``` ## 使用 Selenium 滾動網頁捲軸(scrollBar) 參考:[Python Selenium控制网页滚动条缓慢滚动](https://www.pythonf.cn/read/89) 通過driver.execute()執行js代碼,達到目的。 1. ```python= driver.execute_script('window.scrollBy(0,1000)') ``` scrollBy(x,y)中,x為必須參數,表示向右滾動的像素值;y也為必須參數,表示向下滾動的像素值 2. ```python= driver.execute_script('window.scrollTo(0,1000)') ``` scrollTo(x,y) 中,x為必要參數,表示要在窗口文檔顯示區左上角顯示的文檔的x坐標;y也為必要參數,表示要在窗口文檔顯示區左上角顯示的文檔的y坐標 ## 捲軸長度獲取 參考:[.body.scrollHeight doesn't work in Firefox](https://stackoverflow.com/questions/15932650/body-scrollheight-doesnt-work-in-firefox) 可以在 Chrome 的監控視窗的Console 下指令取得。 兩個指令皆可使用,但在某些網站(如:Youtube)這兩個指令,可能會有其中一個失效。 ```javascript= document.body.scrollHeight ``` ```javascript= document.documentElement.scrollHeight ``` ![](https://i.imgur.com/WAyfefo.png) selenium 捲軸拉到底: 方法一: ```python= driver.execute_script('window.scrollTo(0,document.body.scrollHeight)') ``` 方法二: ```python= driver.execute_script('window.scrollTo(0,document.documentElement.scrollHeight)') ```