10 selenium的基本使用
一、selenium簡介
Selenium是一個用于web自動化測試的工具,Selenium測試直接運行在瀏覽器中,就好像一個真正的用戶在操作一樣, 支持大部分主流的瀏覽器,包括IE(7,8,9,10,11),Firefox,Safari,Chrome,Opera等。
?我們可以利用它來模擬用戶點擊訪問網站,繞過一些復雜的認證場景
通過selnium+驅動瀏覽器這種組合可以直接渲染解析js,繞過大部分的參數構造和反爬。
注意事項:
新版本的Selenium已經不在支持phantomjs,原作者也已經放棄維護該項目了。
還有在做爬蟲的時候盡量不要用這種方法,Selenium+瀏覽器的組合速度慢,應付不了數據量比較大的爬取以及并發爬取。并且很吃電腦資源。
二、selenium配置
Selenium安裝非常簡單,直接pip就可以搞定: pip install selenium
使用selenium驅動chrome瀏覽器需要下載chromedriver,而且chromedriver版本需要與chrome的版本對應,版本錯誤的話則會運行報錯。
Chromedriver下載地址:https://chromedriver.storage.googleapis.com/index.html
Windows: 下載對應版本的chromedriver解壓后,將文件移動到一個配置了環境變量的文件夾中,例如Python安裝文件夾
Linux/Mac: 解壓后,將文件移動至/usr/local/bin目錄中
Selenium驅動其他瀏覽器的也是需要下載對應的驅動
三、基本使用
1、啟動/關閉
獲取頁面內容:page_source
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
# 獲取頁面內容
resp = driver.page_source
print(resp)
time.sleep(5)
driver.close()
2、元素選取
在一個頁面中有很多不同的策略可以定位一個元素。我們可以選擇最合適的方法去查找元素。Selenium提供了下列的方法:
注: 其中的element加上一個s,則是對應的多個元素的查找方法
單個元素查找方法 | 作用 |
---|---|
find_element_by_xpath() | 通過Xpath查找 |
find_element_by_class_name() | 通過class屬性查找 |
find_element_by_css_selector() | 通過css選擇器查找 |
find_element_by_id() | 通過id查找 |
find_element_by_link_text() | 通過鏈接文本查找 |
find_element_by_name() | 通過name屬性進行查找 |
find_element_by_partial_link_text() | 通過鏈接文本的部分匹配查找 |
find_element_by_tag_name() | 通過標簽名查找 |
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
# 通過Xpath查找
a1 = driver.find_element_by_xpath('//input[@value="百度一下"]')
print('find_element_by_xpath',a1)
# 通過id查找
a2 = driver.find_element_by_id('su')
print('find_element_by_id',a2)
# 通過class屬性查找
a3 = driver.find_element_by_class_name('fm')
print('find_element_by_class_name',a3)
# 通過css選擇器查找
a4 = driver.find_element_by_css_selector('#su')
print('find_element_by_class_name',a4)
# 通過鏈接文本查找
a5 = driver.find_element_by_link_text('新聞')
print('find_element_by_link_text',a5)
# 通過name屬性查找
a6 = driver.find_element_by_name('rn')
print('find_element_by_name',a6)
# 通過鏈接文本的部分匹配查找
a7 = driver.find_element_by_partial_link_text('新')
print('find_element_by_partial_link_text',a7)
# 通過標簽名查找
a8 = driver.find_element_by_tag_name('input')
print('find_element_by_tag_name',a8)
# 查找多個
a9 = driver.find_elements_by_tag_name('div')
print('find_elements_by_tag_name',a9)
除了以上的多種查找方式,還有兩種私有方法集成了上面的所有的查找方法,讓我們更方便的使用
方法 | 作用 |
---|---|
find_element(By.XPATH, ‘//button/span’) | 通過Xpath查找一個 |
find_elements(By.XPATH, ‘//button/span’) | 通過Xpath查找多個 |
其中的第一個參數可以選擇使用查找的方法,By.xxx 使用xxx方式解析,解析方法如下:
注:By對象導入: from selenium.webdriver.common.by import By
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
# by_id
a1 = driver.find_element(by=By.ID, value='su')
print('By.ID',a1)
# by_xpath
a2 = driver.find_element(by=By.XPATH, value='//input[@value="百度一下"]')
print('By.XPATH',a2)
# by_link_text
a3 = driver.find_element(by=By.LINK_TEXT, value='新聞')
print('By.LINK_TEXT',a3)
# by_partial_link_text
a4 = driver.find_element(by=By.PARTIAL_LINK_TEXT, value='新')
print('By.PARTIAL_LINK_TEXT',a4)
# by_name
a5 = driver.find_element(by=By.NAME, value='rn')
print('By.NAME',a5)
# by_tag_name
a6 = driver.find_element(by=By.TAG_NAME, value='input')
print('By.NAME',a6)
# by_class_name
a7 = driver.find_element(by=By.CLASS_NAME, value='fm')
print('By.CLASS_NAME',a7)
# by_css_selector
a8 = driver.find_element(by=By.CSS_SELECTOR, value='#su')
print('By.CSS_SELECTOR',a8)
# 查找多個
a9 = driver.find_elements(by=By.TAG_NAME, value='div')
print('find_elements', a9)
三、窗口/頁面/彈窗切換
1、窗口切換
用selenium操作瀏覽器如果需要在打開新的頁面,這個時候會有問題,因為我們用selenium操作的是第一個打開的窗口,所以新打開的頁面我們是無法去操作的,所以我們要用到切換窗口:既handle切換的方法
方法 | 作用 |
---|---|
window_handles | 獲取所有頁面窗口的句柄 |
current_window_handle | 獲取當前頁面窗口的句柄 |
switch_to.window(window_name) | 定位頁面轉到指定的window_name頁面 |
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
time.sleep(10)
# 獲取所有窗口的句柄
handles = driver.window_handles
# 獲取當前窗口的句柄
current = driver.current_window_handle
# 定位頁面轉到指定的window_name頁面
driver.switch_to.window(handles[1])
print(handles)
print(current)
time.sleep(2)
# 獲取當前窗口的句柄
current = driver.current_window_handle
print(current)
2、頁面(frame)切換
在實際的爬蟲中,有時候我們會遇到找不到元素的問題,明明定位的路徑沒問題,這個時候我們可以考慮一下是否是該頁面存在frame的問題導致的定位不到元素。
方法 | 作用 |
---|---|
switch_to.frame(frame_reference) | 切到指定frame,可用id或name(str)、index(int)、元素(WebElement)定位 |
switch_to.parent_frame() | 切到父級frame,如果已是主文檔,則無效果, 相當于后退回去 |
switch_to_default_content() | 切換到主頁面,DOM樹最開始的frame |
3、頁面彈窗
有的時候還會遇到彈窗的問題, 主要有兩種一種是瀏覽器彈窗(alert/prompt),一種是自定義彈窗
自定義彈窗,就是一個自定義的div層,是隱藏頁面中的,當觸發了這個彈窗后,他就顯示出來,這種方式我們通過正常的定位方式是可以定位到的。
alert彈窗,就要用下面的方法處理:
方法 | 作用 |
---|---|
switch_to_alert | 定位到alert彈窗,返回一個彈窗的對象 |
dismiss() | 對彈窗對象的取消操作(相當于點擊彈窗上的取消按鈕) |
accept() | 對彈窗對象的確定操作(相當于點擊彈窗上的確定按鈕) |
send_keys(key) | 對彈窗對象內的輸入框輸入數據(針對于prompt彈窗) |
text | 獲取彈窗內的文本 |
四、等待/動作鏈
1、等待
在selenium操作瀏覽器的過程中,每一次請求url,selenium都會等待頁面加載完成以后,才會將操作權限在交給我們的程序。
但是,由于ajax和各種JS代碼的異步加載問題,當一個頁面被加載到瀏覽器時,該頁面內的元素可以在不同的時間點被加載,這就使得元素的定位變得十分困難,當元素不再頁面中時,使用selenium去查找的時候會拋出ElementNotVisibleException異常。
為了解決這個問題,selenium提供了兩種等待頁面加載的方式,顯示等待和隱式等待,讓我們可以等待元素加載完成后在進行操作。
1)顯示等待
顯示等待: 顯式等待指定某個條件,然后設置最長等待時間,程序每隔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: 超時時返回的信息
WebDriverWait包:from selenium.webdriver.support.wait import WebDriverWait
expected_conditions包:from selenium.webdriver.support import expected_conditions as EC
expected_conditions條件
expected_conditions是selenium的一個子模塊,其中包含一系列可用于判斷的條件,配合該類的方法,就能夠根據條件而進行靈活地等待了
ActionChains提供的方法 | 作用 |
---|---|
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 |
# 顯示等待
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
locator = (By.LINK_TEXT, '貼吧')
try:
# 等待鏈接text'貼吧'出現,然后點擊、打印其href屬性
WebDriverWait(driver,20,0.5).until(EC.presence_of_element_located(locator))
element = driver.find_element(*locator)
print(element.get_attribute('href'))
element.click()
except:
print('發生錯誤')
finally:
driver.close()
2)隱式等待
隱性等待implicitly_wait(xx) :設置了一個最長等待時間,如果在規定時間內網頁加載完成,則執行下一步,否則一直等到時間截止,然后執行下一步。
弊端就是程序會一直等待整個頁面加載完成,就算你需要的元素加載出來了還是需要等待。,也就是一般情況下你看到瀏覽器標簽欄那個小圈不再轉,才會執行下一步,
隱性等待對整個driver的周期都起作用,所以只要設置一次即可
隱性等待和顯性等待可以同時用,但要注意:等待的最長時間取兩者之中的大者
默認等待時間為0,可以通過下面的方式設置:
# 隱式等待
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) #隱式等待,最長10s
driver.get('https://www.baidu.com')
2、動作鏈(ActionChains)
在selenium當中除了簡單的點擊動作外,還有一些稍微復雜的動作,就需要用到ActionChains(動作鏈)這個子模塊來滿足我們的需求。
ActionChains可以完成復雜一點的頁面交互行為,例如元素的拖拽,鼠標移動,懸停行為,內容菜單交互。
它的執行原理就是當調用ActionChains方法的時候不會立即執行,而是將所有的操作暫時儲存在一個隊列中,當調用perform()方法的時候,會按照隊列中放入的先后順序執行前面的操作。
ActionChains包:from selenium.webdriver.common.action_chains import ActionChains
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() | 執行鏈中的所有動作 |
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
try:
driver.get('http://www.treejs.cn/v3/demo/cn/exedit/drag.html')
locator1 = (By.ID,'treeDemo_2_span')
locator2 = (By.ID,'treeDemo_3_span')
locator3 = (By.ID,'treeDemo_11_span')
element = WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator1))
target1 = WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator2))
target2 = WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator3))
ActionChains(driver).drag_and_drop(element,target1).perform()
time.sleep(5)
ActionChains(driver).drag_and_drop(element,target2).perform()
time.sleep(5)
ActionChains(driver).move_to_element(target2)
time.sleep(5)
finally:
driver.quit()
3、微博登錄
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
url = 'https://weibo.com/'
driver.get(url)
locator1 = (By.ID, 'loginname')
locator2 = (By.XPATH, '//div[@class="input_wrap"]/input[@name="password"]')
locator3 = (By.XPATH,'//a[@class="W_btn_a btn_32px"]')
username = WebDriverWait(driver,20,0.5).until(EC.presence_of_element_located(locator1))
password = WebDriverWait(driver,20,0.5).until(EC.presence_of_element_located(locator2))
login = WebDriverWait(driver,20,0.5).until(EC.presence_of_element_located(locator3))
username.send_keys('username')
password.send_keys('password')
login.click()