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