Selenium
是什么?一句話,自動(dòng)化測(cè)試工具。它支持各種瀏覽器,包括
Chrome
,
Safari
,
Firefox
等主流界面式瀏覽器,如果你在這些瀏覽器里面安裝一個(gè)
Selenium
的插件,那么便可以方便地實(shí)現(xiàn)
Web
界面的測(cè)試。換句話說叫
Selenium
支持這些瀏覽器驅(qū)動(dòng)。話說回來,
PhantomJS
不也是一個(gè)瀏覽器嗎,那么
Selenium
支持不?答案是肯定的,這樣二者便可以實(shí)現(xiàn)無縫對(duì)接了。
然后又有什么好消息呢?Selenium
支持多種語言開發(fā),比如
Java
,
C
,
Ruby
等等,有
Python
嗎?那是必須的!哦這可真是天大的好消息啊。
嗯,所以呢?安裝一下 Python
的
Selenium
庫(kù),再安裝好
PhantomJS
,不就可以實(shí)現(xiàn)
Python
+
Selenium
+
PhantomJS
的無縫對(duì)接了嘛!
PhantomJS
用來渲染解析
JS
,
Selenium
用來驅(qū)動(dòng)以及與
Python
的對(duì)接,
Python
進(jìn)行后期的處理,完美的三劍客!
有人問,為什么不直接用瀏覽器而用一個(gè)沒界面的 PhantomJS
呢?答案是:效率高!
Selenium
有兩個(gè)版本,目前最新版本是
2.53.1
(
2016/3/22
)
Selenium
Selenium 2
,又名
WebDriver
,它的主要新功能是集成了
Selenium 1.0
以及
WebDriver
(
WebDriver
曾經(jīng)是
Selenium
的競(jìng)爭(zhēng)對(duì)手)。也就是說
Selenium 2
是
Selenium
和
WebDriver
兩個(gè)項(xiàng)目的合并,即
Selenium 2
兼容
Selenium
,它既支持
Selenium API
也支持
WebDriver API
。
查看詳情說明 Webdriver
嗯,通過以上描述,我們應(yīng)該對(duì) Selenium
有了大概對(duì)認(rèn)識(shí),接下來就讓我們開始進(jìn)入動(dòng)態(tài)爬取的新世界吧。
本文參考內(nèi)容來自 Selenium
官網(wǎng)
SeleniumPython
文檔
安裝
首先安裝 Selenium
pip install selenium
快速開始
初步體驗(yàn)
我們先來一個(gè)小例子感受一下 Selenium
,這里我們用
Chrome
瀏覽器來測(cè)試,方便查看效果,到真正爬取的時(shí)候換回
PhantomJS
即可。
from selenium
import webdriver
browser = webdriver.Chrome()
browser.
get('http://www.baidu.com/')
運(yùn)行這段代碼,會(huì)自動(dòng)打開瀏覽器,然后訪問百度。
如果程序執(zhí)行錯(cuò)誤,瀏覽器沒有打開,那么應(yīng)該是沒有裝 Chrome
瀏覽器或者
Chrome
驅(qū)動(dòng)沒有配置在環(huán)境變量里。下載驅(qū)動(dòng),然后將驅(qū)動(dòng)文件路徑配置在環(huán)境變量即可。
瀏覽器驅(qū)動(dòng)下載:
https://sites.google.com/a/chromium.org/chromedriver/downloads
比如我的是 Mac OS
,就把下載好的文件放在
/usr/bin
目錄下就可以了。
模擬提交
下面的代碼實(shí)現(xiàn)了模擬提交提交搜索的功能,首先等頁(yè)面加載完成,然后輸入到搜索框文本,點(diǎn)擊提交。
from selenium
import webdriver
from selenium.webdriver.common.keys
import Keys
driver = webdriver.Chrome()
driver.
get("http://www.python.org")assert "Python"
in driver.title
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)print driver.page_source
同樣是在 Chrome
里面測(cè)試,感受一下。
實(shí)例說明
其中 driver.get
方法會(huì)打開請(qǐng)求的
URL
,
WebDriver
會(huì)等待頁(yè)面完全加載完成之后才會(huì)返回,即程序會(huì)等待頁(yè)面的所有內(nèi)容加載完成,
JS
渲染完畢之后才繼續(xù)往下執(zhí)行。注意:如果這里用到了特別多的
Ajax
的話,程序可能不知道是否已經(jīng)完全加載完畢。
WebDriver
提供了許多尋找網(wǎng)頁(yè)元素的方法,譬如
find_element_by_*
的方法。例如一個(gè)輸入框可以通過
find_element_by_name
方法尋找
name
屬性來確定。然后我們輸入來文本然后模擬點(diǎn)擊了回車,就像我們敲擊鍵盤一樣。我們可以利用
Keys
這個(gè)類來模擬鍵盤輸入。
最后最重要的一點(diǎn),獲取網(wǎng)頁(yè)渲染后的源代碼,輸出 page_source
屬性即可。
這樣,我們就可以做到網(wǎng)頁(yè)的動(dòng)態(tài)爬取了。
測(cè)試用例
有了以上特性,我們當(dāng)然可以用來寫測(cè)試樣例了。
import unittest
from selenium
import webdriver
from selenium.webdriver.common.keys
import Keys
class
PythonOrgSearch(unittest.TestCase):
def
setUp(self):
self.driver = webdriver.Chrome()
def
test_search_in_python_org(self):
driver = self.driver
driver.get("http://www.python.org")
self.assertIn("Python", driver.title)
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found."
not
in driver.page_source
def
tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
運(yùn)行程序,同樣的功能,我們將其封裝為測(cè)試標(biāo)準(zhǔn)類的形式。
測(cè)試用例
測(cè)試用例是繼承了 unittest.TestCase
類,繼承這個(gè)類表明這是一個(gè)測(cè)試類。
setUp
方法是初始化的方法,這個(gè)方法會(huì)在每個(gè)測(cè)試類中自動(dòng)調(diào)用。每一個(gè)測(cè)試方法命名都有規(guī)范,必須以
test
開頭,會(huì)自動(dòng)執(zhí)行。最后的
tearDown
方法會(huì)在每一個(gè)測(cè)試方法結(jié)束之后調(diào)用。這相當(dāng)于最后的析構(gòu)方法。在這個(gè)方法里寫的是
close
方法,你還可以寫
quit
方法。不過
close
方法相當(dāng)于關(guān)閉了這個(gè)
TAB
選項(xiàng)卡,然而
quit
是退出了整個(gè)瀏覽器。當(dāng)你只開啟了一個(gè)
TAB
選項(xiàng)卡的時(shí)候,關(guān)閉的時(shí)候也會(huì)將整個(gè)瀏覽器關(guān)閉。
頁(yè)面操作
頁(yè)面交互
僅僅抓取頁(yè)面沒有多大卵用,我們真正要做的是做到和頁(yè)面交互,比如點(diǎn)擊,輸入等等。那么前提就是要找到頁(yè)面中的元素。WebDriver
提供了各種方法來尋找元素。例如下面有一個(gè)表單輸入框。
<input
type="text" name="passwd" id="passwd-id" />
我們可以這樣獲取它
element = driver.find_element_by_id("passwd-id")element = driver.find_element_by_name("passwd")element = driver.find_elements_by_tag_name("input")element = driver.find_element_by_xpath("http://input[@id='passwd-id']")
你還可以通過它的文本鏈接來獲取,但是要小心,文本必須完全匹配才可以,所以這并不是一個(gè)很好的匹配方式。
而且你在用 xpath
的時(shí)候還需要注意的是,如果有多個(gè)元素匹配了
xpath
,它只會(huì)返回第一個(gè)匹配的元素。如果沒有找到,那么會(huì)拋出
NoSuchElementException
的異常。
獲取了元素之后,下一步當(dāng)然就是向文本輸入內(nèi)容了,可以利用下面的方法
element.send_keys("some text")
同樣你還可以利用 Keys
這個(gè)類來模擬點(diǎn)擊某個(gè)按鍵。
element.send_keys("and some", Keys.ARROW_DOWN)
你可以對(duì)任何獲取到到元素使用 send_keys
方法,就像你在
GMail
里面點(diǎn)擊發(fā)送鍵一樣。不過這樣會(huì)導(dǎo)致的結(jié)果就是輸入的文本不會(huì)自動(dòng)清除。所以輸入的文本都會(huì)在原來的基礎(chǔ)上繼續(xù)輸入。你可以用下面的方法來清除輸入文本的內(nèi)容。
element.clear()
這樣輸入的文本會(huì)被清除。
填充表單
我們已經(jīng)知道了怎樣向文本框中輸入文字,但是其它的表單元素呢?例如下拉選項(xiàng)卡的的處理可以如下
element = driver.find_element_by_xpath("http://select[@name='name']")
all_options = element.find_elements_by_tag_name("option")
for
option
in all_options:
print("Value is: %s" %
option.get_attribute("value"))
option.click()
首先獲取了第一個(gè) select
元素,也就是下拉選項(xiàng)卡。然后輪流設(shè)置了
select
選項(xiàng)卡中的每一個(gè)
option
選項(xiàng)。你可以看到,這并不是一個(gè)非常有效的方法。
其實(shí) WebDriver
中提供了一個(gè)叫
Select
的方法,可以幫助我們完成這些事情。
from selenium.webdriver.support.ui import
Selectselect =
Select(driver.find_element_by_name('name'))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)
如你所見,它可以根據(jù)索引來選擇,可以根據(jù)值來選擇,可以根據(jù)文字來選擇。是十分方便的。
全部取消選擇怎么辦呢?很簡(jiǎn)單
select =
Select(driver.find_element_by_id('id'))
select.deselect_all()
這樣便可以取消所有的選擇。
另外我們還可以通過下面的方法獲取所有的已選選項(xiàng)。
select = Select(driver.find_element_by_xpath("xpath"))all_selected_options = select.all_selected_options
獲取所有可選選項(xiàng)是
options = select.options
如果你把表單都填好了,最后肯定要提交表單對(duì)吧。怎嗎提交呢?很簡(jiǎn)單
driver.find_element_by_id("submit").click()
這樣就相當(dāng)于模擬點(diǎn)擊了 submit
按鈕,做到表單提交。
當(dāng)然你也可以單獨(dú)提交某個(gè)元素
element.submit()
方法,WebDriver
會(huì)在表單中尋找它所在的表單,如果發(fā)現(xiàn)這個(gè)元素并沒有被表單所包圍,那么程序會(huì)拋出
NoSuchElementException
的異常。
元素拖拽
要完成元素的拖拽,首先你需要指定被拖動(dòng)的元素和拖動(dòng)目標(biāo)元素,然后利用 ActionChains
類來實(shí)現(xiàn)。
element = driver.find_element_by_name("source")
target = driver.find_element_by_name("target")
from selenium.webdriver
import ActionChains
action_chains = ActionChains(driver)
action_chains.drag_and_drop(element, target).perform()
這樣就實(shí)現(xiàn)了元素從 source
拖動(dòng)到
target
的操作。
頁(yè)面切換
一個(gè)瀏覽器肯定會(huì)有很多窗口,所以我們肯定要有方法來實(shí)現(xiàn)窗口的切換。切換窗口的方法如下
driver.switch_to_window("windowName")
另外你可以使用 window_handles
方法來獲取每個(gè)窗口的操作對(duì)象。例如
for handle
in driver.window_handles:
driver.switch_to_window(handle)
另外切換 frame
的方法如下
driver.switch_to_frame("
frameName.0.child")
這樣焦點(diǎn)會(huì)切換到一個(gè) name
為
child
的
frame
上。
彈窗處理
當(dāng)你出發(fā)了某個(gè)事件之后,頁(yè)面出現(xiàn)了彈窗提示,那么你怎樣來處理這個(gè)提示或者獲取提示信息呢?
alert = driver.switch_to_alert()
通過上述方法可以獲取彈窗對(duì)象。
歷史記錄
那么怎樣來操作頁(yè)面的前進(jìn)和后退功能呢?
driver.forward()
driver.back()
嗯,簡(jiǎn)潔明了。
Cookies處理
為頁(yè)面添加 Cookies
,用法如下
# Go to the correct domain
driver.get("http://www.example.com")
# Now set the cookie. This one's valid for the entire domain
cookie = {‘name’ : ‘foo’, ‘value’ : ‘bar’}
driver.add_cookie(cookie)
獲取頁(yè)面 Cookies
,用法如下
# Go to the correct domain
driver.get("http://www.example.com")
# And now output all the available cookies for the current URL
driver.get_cookies()
以上便是 Cookies
的處理,同樣是非常簡(jiǎn)單的。
元素選取
關(guān)于元素的選取,有如下的API
單個(gè)元素選取
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector
多個(gè)元素選取
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
另外還可以利用 By
類來確定哪種選擇方式
from selenium.webdriver.common.
by import
By
driver.find_element(
By.XPATH, '//button[text()="Some text"]')
driver.find_elements(
By.XPATH, '//button')
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"
更詳細(xì)的元素選擇方法參見官方文檔
元素選擇:
http://selenium-python.readthedocs.org/en/latest/locating-elements.html
頁(yè)面等待
這是非常重要的一部分,現(xiàn)在的網(wǎng)頁(yè)越來越多采用了 Ajax
技術(shù),這樣程序便不能確定何時(shí)某個(gè)元素完全加載出來了。這會(huì)讓元素定位困難而且會(huì)提高產(chǎn)生
ElementNotVisibleException
的概率。
所以 Selenium
提供了兩種等待方式,一種是隱式等待,一種是顯式等待。
隱式等待是等待特定的時(shí)間,顯式等待是指定某一條件直到這個(gè)條件成立時(shí)繼續(xù)執(zhí)行。
顯式等待
顯式等待指定某個(gè)條件,然后設(shè)置最長(zhǎng)等待時(shí)間。如果在這個(gè)時(shí)間還沒有找到元素,那么便會(huì)拋出異常了。
from selenium import webdriver
from selenium.webdriver.common.
by import
Byfrom selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
as EC
driver = webdriver.Chrome()
driver.
get("http://somedomain/url_that_delays_loading")
try:
element = WebDriverWait(driver, 10).
until(
EC.presence_of_element_located((
By.ID, "myDynamicElement"))
)
finally:
driver.quit()
程序默認(rèn)會(huì) 500ms
調(diào)用一次來查看元素是否已經(jīng)生成,如果本來元素就是存在的,那么會(huì)立即返回。
下面是一些內(nèi)置的等待條件,你可以直接調(diào)用這些條件,而不用自己寫某些等待條件了。
等待條件
title_is
title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable – it is Displayed and Enabled.
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present
from selenium.webdriver.sup
port import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID,'someid')))
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID,'someid')))
隱式等待
隱式等待比較簡(jiǎn)單,就是簡(jiǎn)單地設(shè)置一個(gè)等待時(shí)間,單位為秒。
from selenium
import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
當(dāng)然如果不設(shè)置,默認(rèn)等待時(shí)間為0
。
程序框架
對(duì)于頁(yè)面測(cè)試和分析,官方提供了一個(gè)比較明晰的代碼結(jié)構(gòu),可以參考。
結(jié)語
以上就是 Selenium
的基本用法,我們講解了頁(yè)面交互,頁(yè)面渲染之后的源代碼的獲取。這樣,即使頁(yè)面是
JS
渲染而成的,我們也可以手到擒來了。就是這么溜!
來源:網(wǎng)絡(luò)