爬虫学习笔记(十六)—— Selenium
Selenium是一個主要用于Web應用程序自動化測試的工具集合,在行業內已經得到廣泛的應用。
文章目錄
- 一、簡介
- 1.1、作用
- 1.2、運行環境
- 1.3、注意事項
- 二、基本使用
- 2.1、原理
- 2.2、安裝
- 2.2.1、selenium安裝
- 2.2.2、瀏覽器驅動安裝
- 2.3、元素選取
- 2.3.1、find_element(s)_by__...方法
- 2.3.2、By對象查找
- 2.3.3、文本輸入/提交
- 三、動作切換
- 3.1、窗口切換
- 3.2、頁面(frame)切換
- 3.2、頁面彈窗
- 四、等待
- 4.1、簡介
- 4.2、顯式等待
- 4.3、expected_conditions條件
- 4.4、隱性等待
- 4.5、強制等待
- 4.6、代碼示例
- 五、動作鏈
- 六、補充知識點
- 6.1、常用方法
- 6.2、無界面設置
一、簡介
1.1、作用
通過它,我們可以寫出自動化程序,模擬瀏覽器里操作web界面。 比如點擊界面按鈕,在文本框中輸入文字等操作。
而且還能從web界面獲取信息。 比如招聘網站職位信息,財經網站股票價格信息 等等,然后用程序進行分析處理。
1.2、運行環境
Selenium測試直接運行在瀏覽器中,就好像一個真正的用戶在操作一樣, 支持大部分主流的瀏覽器,包括IE,Firefox,Safari,Chrome,Opera等。
我們可以利用它來模擬用戶點擊訪問網站,繞過一些復雜的認證場景。
通過selnium+驅動瀏覽器這種組合可以直接渲染解析js,繞過大部分的參數構造和反爬。
1.3、注意事項
新版本的Selenium已經不在支持phantomjs,原作者也已經放棄維護該項目了。
還有在做爬蟲的時候盡量不要用這種方法,Selenium+瀏覽器的組合速度慢,應付不了數據量比較大的爬取以及并發爬取。并且很吃電腦資源。
二、基本使用
2.1、原理
1. WebDriver API(基于Java、Python、C#等語言)
對于Python語言來說,就是下載下來的selenium庫。
2. 瀏覽器的驅動(browser driver)
每個瀏覽器都有自己的驅動,均以exe文件形式存在
https://chromedriver.storage.googleapis.com/index.html
比如谷歌的chromedriver.exe、火狐的geckodriver.exe、IE的IEDriverServer.exe
3. 瀏覽器
瀏覽器當然就是我們很熟悉的常用的各種瀏覽器。
那在WebDriver腳本運行的時候,它們之間是如何通信的呢?為什么同一個browser driver即可以處理java語言的腳本,也可以處理python語言的腳本呢?
讓我們來看一下,一條Selenium腳本執行時后端都發生了哪些事情:
2.2、安裝
2.2.1、selenium安裝
終端輸入命令:
pip install selenium2.2.2、瀏覽器驅動安裝
chrome驅動下載地址:https://chromedriver.storage.googleapis.com/index.html
注意:每個驅動該對應每個瀏覽器;有時候瀏覽器會自動升級,導致瀏覽器不可用;
2.3、元素選取
2.3.1、find_element(s)by_…方法
在一個頁面中有很多不同的策略可以定位一個元素。我們可以選擇最合適的方法去查找元素。Selenium提供了下列的方法:
| find_element_by_xpath() | 通過Xpath查找 |
| find_element_by_class_name() | 通過class屬性查找 |
| find_element_by_id() | 通過id屬性查找 |
| find_element_by_name() | 通過name屬性進行查找 |
| find_element_by_css_selector() | 通過css選擇器查找 語法規則 |
| find_element_by_link_text() | 通過鏈接文本查找 |
| find_element_by_partial_link_text() | 通過鏈接文本的部分匹配查找 |
| find_element_by_tag_name() | 通過標簽名查找 |
注: 其中的element加上一個s,則是對應的多個元素的查找方法.
示例:
注意:webdriver.Chrome()的參數是你驅動的位置,當然如果有放到python文件的Scripts下或者有添加到環境變量中就可以不用寫
結果展示:
2.3.2、By對象查找
By對象導入: from selenium.webdriver.common.by import By
除了以上的多種查找方式,還有兩種私有方法集成了上面的所有的查找方法,讓我們更方便的使用:
| find_element(By.XPATH, ‘//button/span’) | 通過Xpath查找一個 |
| find_elements(By.XPATH, ‘//button/span’) | 通過Xpath查找多個 |
其中的第一個參數可以選擇使用查找的方法,By.xxx使用xxx方式解析,解析方法如下:
- ID:通過id屬性查找
- XPATH :通過Xpath查找
- LINK_TEXT:通過鏈接文本查找
- PARTIAL_LINK_TEXT :通過鏈接文本的部分匹配查找
- NAME:通過name屬性進行查找
- TAG_NAME:通過標簽名查找
- CLASS_NAME:通過class屬性查找
- CSS_SELECTOR :通過css選擇器查找
示例:
from selenium import webdriver from selenium.webdriver.common.by import Bywb = webdriver.Chrome() #驅動已經添加到環境變量,所以可以不用寫參數 wb.get('https://www.51zxw.net/List.aspx?cid=451')#通過Xpath查找 home_page = wb.find_element(By.XPATH,'//div[@class="headLinks ml-10"]/a') home_page.click()結果展示:
2.3.3、文本輸入/提交
當我們需要通過selenium完成一個在網站中進行搜索的功能,前面我們已經知道了如何選取定位一個元素了,假如是定位到了一個輸入框的元素,那么我們就要進行查詢數據的輸入和提交了。
| send_keys() | 文本輸入 |
| click() | 文本提交 |
示例:
from selenium import webdriver from selenium.webdriver.common.by import Bywb = webdriver.Chrome() wb.get('https://www.51zxw.net/List.aspx?cid=451')#通過id屬性定位到搜索框 home_page = wb.find_element_by_id("keyWordsT") #搜索框輸入 Python home_page.send_keys('Python') #定位到搜索按鈕 submitbtn = wb.find_element_by_xpath('//button[@type="submit"]') #點擊搜索按鈕 submitbtn.click()結果展示:
三、動作切換
3.1、窗口切換
在開始講解之前我們先來看一個示例:
from selenium import webdriver from selenium.webdriver.common.by import Bywb = webdriver.Chrome() wb.get('https://www.51zxw.net/List.aspx?cid=451') print(wb.title)openjs = 'window.open("https://www.csdn.net/")' wb.execute_script(openjs) print(wb.title)結果:
程序開發-我要自學網 程序開發-我要自學網通過結果我們可以看到,雖然瀏覽器給我們打開了第二個窗口并且停留在第二個窗口,但是其本質還是停留在第一個打開的窗口,因此我們需要進行窗口切換。
用selenium操作瀏覽器如果需要在打開新的頁面,這個時候會有這個問題,因為我們用selenium操作的是第一個打開的窗口,所以新打開的頁面我們是無法去操作的,所以我們要用到切換窗口:即handle切換的方法
| widgetjs = ‘window.open(“https://www.baidu.com”);’ chrome.execute_script(widgetjs ) | 打開新標簽 |
| window_handles | 獲取所有頁面窗口的句柄 |
| current_window_handle | 獲取當前頁面窗口的句柄 |
| switch_to.window(window_name) | 定位頁面轉到指定的window_name頁面 |
注意:
- window_handles 的順序并不是瀏覽器上標簽的順序,盡量避免多標簽操作
示例:
from selenium import webdriverwb = webdriver.Chrome() wb.get('https://www.51zxw.net/List.aspx?cid=451') print(wb.title) #當前打印窗口的標題openjs = 'window.open("https://www.csdn.net/")' wb.execute_script(openjs) print(wb.title) #當前打印窗口的標題print('所有頁面窗口的句柄: ',wb.window_handles) #打印所有頁面窗口的句柄 print('當前頁面窗口的句柄: ',wb.current_window_handle) #獲取當前頁面窗口的句柄wb.switch_to.window(wb.window_handles[1]) #通過上面打印比較 定位頁面轉到第二個窗口頁面 print(wb.title) #獲取當前頁面窗口的句柄結果:
程序開發-我要自學網 程序開發-我要自學網 #我們還沒切換句柄,所以是還是第一個窗口 所有頁面窗口的句柄: ['CDwindow-4ACAD14BE66BD92795930B70D8CA1FA7', 'CDwindow-C1D8821CFEBFE3A0C0AF456231EF2CB3'] 當前頁面窗口的句柄: CDwindow-4ACAD14BE66BD92795930B70D8CA1FA7 CSDN - 專業開發者社區由于很難知道其他頁面對應的handle是什么,如果只有少數的標簽也許還能應付,但是當打開了相當多標簽時就很難對他們進行處理了,所以要盡量避免多標簽的操作。
3.2、頁面(frame)切換
在實際的爬蟲中,明明定位的路徑沒問題,這個時候我們可以考慮一下是否是該頁面存在frame的問題導有時候我們會遇到找不到元素的問題致的定位不到元素。
知識點補充:什么是frame呢?
frame是一個框架標簽,通過使用框架可以在一個瀏覽器窗口中顯示不止一個頁面,也就是說,在一個窗口中展示多個頁面,每個頁面稱之為一個框架,并且每個框架獨立于其他的框架。這個frame標簽一共有三種,分別是frameset、frame、iframe,frameset跟其他普通標簽沒有區別,不會影響到正常的定位,而frame與iframe對selenium定位而言是一樣的,selenium有一組方法對frame進行操作。reference是傳入的參數,用來定位frame,可以傳入id、name、index以及selenium的WebElement對象
| switch_to.frame(frame_reference) | 切到指定frame,可用id或name(str)、index(int)、元素(WebElement)定位 |
| switch_to.parent_frame() | 切到父級frame,如果已是主文檔,則無效果, 相當于后退回去 |
| switch_to.default_content() | 切換到主頁面,DOM樹最開始的frame |
簡單示例:
import time from selenium import webdriverwb = webdriver.Chrome() wb.get('https://study.163.com/')# 關閉頁面彈出的小窗口 ok_btn = wb.find_element_by_xpath('//span[@class="ux-btn th-bk-main ux-btn- ux-btn- ux-modal-btn um-modal-btn_ok th-bk-main"]') ok_btn.click() # 登錄窗口 login_btn = wb.find_element_by_xpath('//div[@class="go-login f-ib th-fs0fc6"]') login_btn.click()time.sleep(2) #先等2秒,代碼執行過快,登錄輸入框加載需要時間,否則會出錯 # 切換frame fr = wb.find_element_by_xpath('//iframe[@frameborder="0"]') wb.switch_to.frame(fr)# 賬號輸入框輸入 name_input = wb.find_element_by_id('phoneipt') name_input.send_keys('123456789') # 密碼輸入 pwd_input = wb.find_element_by_xpath('//input[@class="j-inputtext dlemail"]') pwd_input.send_keys('10987654321')結果展示:
3.2、頁面彈窗
有的時候還會遇到彈窗的問題, 主要有兩種一種是瀏覽器彈窗(alert/prompt),另一種是自定義彈窗。自定義彈窗,就是一個自定義的div層,是隱藏頁面中的,當觸發了這個彈窗后,他就顯示出來,這種方式我們通過正常的定位方式是可以定位到的。
alert彈窗,就要用下面的方法處理:
| switch_to.alert | 定位到alert彈窗,返回一個彈窗的對象 |
| dismiss() | 對彈窗對象的取消操作(相當于點擊彈窗上的取消按鈕) |
| accept() | 對彈窗對象的確定操作(相當于點擊彈窗上的確定按鈕) |
| send_keys(key) | 對彈窗對象內的輸入框輸入數據(針對于prompt彈窗) |
| text | 獲取彈窗內的文本 |
示例:
測試html代碼:
效果:
python代碼:
import time from selenium import webdriverwb = webdriver.Chrome() wb.get(r'D:\html test\一些示例\ex.html') # alert 彈窗 wb.find_element_by_id("alert").click() alert_tag = wb.switch_to.alert print(alert_tag.text) time.sleep(2) #稍微延遲下,看得比較直觀 alert_tag.accept()# prompt 彈窗 # # wb.find_element_by_id("prompt").click() # prompt_tag = wb.switch_to.alert # print(prompt_tag.text) # prompt_tag.send_keys('hello world!!!') # time.sleep(2) #稍微延遲下,看得比較直觀 # prompt_tag.accept()# confirm 彈窗 # # wb.find_element_by_id("confirm").click() # confirm_tag = wb.switch_to.alert # print(confirm_tag.text) # time.sleep(2) #稍微延遲下,看得比較直觀 # confirm_tag.accept()結果展示(這里我就只演示一個):
打印結果:
this is alert!!!四、等待
4.1、簡介
在selenium操作瀏覽器的過程中,每一次請求url,selenium都會等待頁面加載完成以后,才會將操作權限在交給我們的程序。
?但是,由于ajax和各種JS代碼的異步加載問題,當一個頁面被加載到瀏覽器時,該頁面內的元素可以在不同的時間點被加載,這就使得元素的定位變得十分困難,當元素不再頁面中時,使用selenium去查找的時候會拋出ElementNotVisibleException異常。
? 為了解決這個問題,selenium提供了兩種等待頁面加載的方式,顯示等待和隱式等待,讓我們可以等待元素加載完成后在進行操作。
4.2、顯式等待
顯式等待: 顯式等待指定某個條件,然后設置最長等待時間,程序每隔XX時間看一眼,如果條件成立,則執行下一步,否則繼續等待,直到超過設置的最長時間,然后拋出超時異常(TimeoutException)。
顯示等待主要使用了WebDriverWait類與expected_conditions模塊。
一般寫法:
WebDriverWait(driver, timeout, poll_frequency, igonred_exceptions).until(method, message)參數:
- Driver:傳入WebDriver實例。
- timeout: 超時時間,等待的最長時間(同時要考慮隱性等待時間)
- poll_frequency: 調用until中的方法的間隔時間,默認是0.5秒
- ignored_exceptions: 忽略的異常,如果在調用until的過程中拋出這個元組中的異常,則不中斷代碼,繼續等待。
- Method:可執行方法
- Message:超時時返回的信息
4.3、expected_conditions條件
expected_conditions是selenium的一個子模塊,其中包含一系列可用于判斷的條件,配合該類的方法,就能夠根據條件而進行靈活地等待了。
| title_is title_contains | 這兩個條件類驗證title,驗證傳入的參數title是否等于或包含于driver |
| presence_of_element_located presence_of_all_elements_located | 這兩個條件驗證元素是否出現,傳入的參數都是元組類型的locator,如(By.ID, ‘kw’)顧名思義,一個只要一個符合條件的元素加載出來就通過;另一個必須所有符合條件的元素都加載出來才行 |
| visibility_of_element_located invisibility_of_element_located visibility_of | 這三個條件驗證元素是否可見,前兩個傳入參數是元組類型的locator,第三個傳入WebElement |
| text_to_be_present_in_element text_to_be_present_in_element_value | 判斷某段文本是否出現在某元素中,一個判斷元素的text,一個判斷元素的value |
| frame_to_be_available_and_switch_to_it | 判斷frame是否可切入,可傳入locator元組或者直接傳入定位方式:id、name、index或WebElement |
| alert_is_present | 判斷是否有alert出現 |
| element_to_be_clickable | 判斷元素是否可點擊,傳入locator |
4.4、隱性等待
隱性等待implicitly_wait(xx) :設置了一個最長等待時間,如果在規定時間內網頁加載完成,則執行下一步,否則一直等到時間截止,然后執行下一步。
弊端就是程序會一直等待整個頁面加載完成,就算你需要的元素加載出來了還是需要等待。,也就是一般情況下你看到瀏覽器標簽欄那個小圈不再轉,才會執行下一步。
隱性等待對整個driver的周期都起作用,所以只要設置一次即可;
隱性等待和顯性等待可以同時用,但要注意:等待的最長時間取兩者之中的大者;
默認等待時間為0,可以通過下面的方式設置:
from selenium import webdriver driver = webdriver.Chrome() ······ driver.implicitly_wait(10) #隱式等待,最長10s ······示例:
4.5、強制等待
強制等待就是不論如何,在此處都需要阻塞等待一段時間,使用方式如下:
import time time.sleep(3) #等待3秒4.6、代碼示例
from selenium import webdriver import time from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import Bywb = webdriver.Chrome() wb.get('https://study.163.com/')# 關閉頁面彈出的小窗口 ok_btn = wb.find_element_by_xpath('//span[@class="ux-btn th-bk-main ux-btn- ux-btn- ux-modal-btn um-modal-btn_ok th-bk-main"]') ok_btn.click() # 登錄窗口 login_btn = wb.find_element_by_xpath('//div[@class="go-login f-ib th-fs0fc6"]') login_btn.click()#設置顯示等待 locater = (By.XPATH,'//iframe[@frameborder="0"]') WebDriverWait(driver=wb,timeout=3,poll_frequency=0.4).until(EC.presence_of_element_located(locater),message='找不到元素!') #隱式等待 最長3s # wb.implicitly_wait(3) # 強制等待 3s # time.sleep(3) # 切換frame 加載需要時間,所以要設等待 fr = wb.find_element_by_xpath('//iframe[@frameborder="0"]') wb.switch_to.frame(fr) # 賬號輸入框輸入 name_input = wb.find_element_by_id('phoneipt') name_input.send_keys('123456789') # 密碼輸入 pwd_input = wb.find_element_by_xpath('//input[@class="j-inputtext dlemail"]') pwd_input.send_keys('10987654321')五、動作鏈
在selenium當中除了簡單的點擊動作外,還有一些稍微復雜的動作,就需要用到ActionChains(動作鏈)這個子模塊來滿足我們的需求。
ActionChains可以完成復雜一點的頁面交互行為,例如元素的拖拽,鼠標移動,懸停行為,內容菜單交互。
它的執行原理就是當調用ActionChains方法的時候不會立即執行,而是將所有的操作暫時儲存在一個隊列中,當調用perform()方法的時候,會按照隊列中放入的先后順序執行前面的操作。
ActionChains包:from selenium.webdriver.common.action_chains import ActionChains
| click(on_element=None) | 鼠標左鍵單擊 |
| double_click(on_element=None) | 雙擊鼠標左鍵 |
| context_click(on_element=None) | 點擊鼠標右鍵 |
| click_and_hold(on_element=None) | 點擊鼠標左鍵,按住不放 |
| release(on_element=None) | 在某個元素位置松開鼠標左鍵 |
| drag_and_drop(source, target) | 拖拽到某個元素然后松開 |
| drag_and_drop_by_offset(source, xoffset, yoffset) | 拖拽到某個坐標然后松開 |
| move_to_element(to_element) | 鼠標移動到某個元素 |
| move_by_offset(xoffset, yoffset) | 移動鼠標到指定的x,y位置 |
| move_to_element_with_offset(to_element, xoffset, yoffset) | 將鼠標移動到距某個元素多少距離的位置 |
| perform() | 執行鏈中的所有動作 |
示例:
import time from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChainsdriver = webdriver.Chrome()driver.get('http://www.treejs.cn/v3/demo/cn/exedit/drag.html') time.sleep(2) element1 = driver.find_element_by_id('treeDemo_2_span') target = driver.find_element_by_id('treeDemo_3_span') ActionChains(driver).drag_and_drop(element1, target).perform()element2 = driver.find_element_by_id('treeDemo_12_span') ActionChains(driver).drag_and_drop(element2,target).perform()結果展示:
六、補充知識點
6.1、常用方法
| Chrome.refresh() | 刷新頁面 |
| Chrome.close() | 關閉當前標簽 |
| Chrome.quit() | 關閉所有標簽 |
| Chrome.page_source | 網頁源代碼 |
| Chrome.cookies | 本頁保存的cookie |
| Chrome.maximize_window() | 最大化窗口 |
6.2、無界面設置
from selenium.webdriver.chrome.options import Options from selenium import webdriverchrome_options=Options() chrome_options.add_argument("--headless") drive = webdriver.Chrome(options=chrome_options)drive.get('https://www.51zxw.net/List.aspx?cid=451') print(drive.page_source) #獲取網頁源碼總結
以上是生活随笔為你收集整理的爬虫学习笔记(十六)—— Selenium的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爬虫学习笔记(十五)——加密解密
- 下一篇: 爬虫学习笔记(十七)—— 字符验证码