http:///2599.html
前言
在上一節(jié)我們學(xué)習(xí)了 PhantomJS 的基本用法,歸根結(jié)底它是一個(gè)沒有界面的瀏覽器,而且運(yùn)行的是 JavaScript 腳本,然而這就能寫爬蟲了嗎?這又和Python有什么關(guān)系?說好的Python爬蟲呢?庫都學(xué)完了你給我看這個(gè)?客官別急,接下來我們介紹的這個(gè)工具,統(tǒng)統(tǒng)解決掉你的疑惑。
簡介
Selenium 是什么?一句話,自動(dòng)化測(cè)試工具。它支持各種瀏覽器,包括 Chrome,Safari,F(xiàn)irefox 等主流界面式瀏覽器,如果你在這些瀏覽器里面安裝一個(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 庫,再安裝好 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 2,又名 WebDriver,它的主要新功能是集成了 Selenium 1.0 以及 WebDriver(WebDriver 曾經(jīng)是 Selenium 的競(jìng)爭對(duì)手)。也就是說 Selenium 2 是 Selenium 和 WebDriver 兩個(gè)項(xiàng)目的合并,即 Selenium 2 兼容 Selenium,它既支持 Selenium API 也支持 WebDriver API。
更多詳情可以查看 Webdriver 的簡介。
Webdriver
嗯,通過以上描述,我們應(yīng)該對(duì) Selenium 有了大概對(duì)認(rèn)識(shí),接下來就讓我們開始進(jìn)入動(dòng)態(tài)爬取的新世界吧。
本文參考內(nèi)容來自
Selenium官網(wǎng) SeleniumPython文檔
安裝
首先安裝 Selenium
或者下載源碼
下載源碼
然后解壓后運(yùn)行下面的命令進(jìn)行安裝
安裝好了之后我們便開始探索抓取方法了。
快速開始
初步體驗(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)下載
比如我的是 Mac OS,就把下載好的文件放在 /usr/bin 目錄下就可以了。
模擬提交
下面的代碼實(shí)現(xiàn)了模擬提交提交搜索的功能,首先等頁面加載完成,然后輸入到搜索框文本,點(diǎn)擊提交。
|
from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = webdriver.Chrome() driver.get("http://www.") 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è)試,感受一下。
The driver.get method will navigate to a page given by the URL. WebDriver will wait until the page has fully loaded (that is, the “onload” event has fired) before returning control to your test or script. It’s worth noting that if your page uses a lot of AJAX on load then WebDriver may not know when it has completely loaded.
其中 driver.get 方法會(huì)打開請(qǐng)求的URL,WebDriver 會(huì)等待頁面完全加載完成之后才會(huì)返回,即程序會(huì)等待頁面的所有內(nèi)容加載完成,JS渲染完畢之后才繼續(xù)往下執(zhí)行。注意:如果這里用到了特別多的 Ajax 的話,程序可能不知道是否已經(jīng)完全加載完畢。
WebDriver offers a number of ways to find elements using one of the find_element_by_* methods. For example, the input text element can be located by its name attribute using find_element_by_name method
WebDriver 提供了許多尋找網(wǎng)頁元素的方法,譬如 find_element_by_* 的方法。例如一個(gè)輸入框可以通過 find_element_by_name 方法尋找 name 屬性來確定。
Next we are sending keys, this is similar to entering keys using your keyboard. Special keys can be send using Keys class imported from selenium.webdriver.common.keys
然后我們輸入來文本然后模擬點(diǎn)擊了回車,就像我們敲擊鍵盤一樣。我們可以利用 Keys 這個(gè)類來模擬鍵盤輸入。
最后最重要的一點(diǎn)
獲取網(wǎng)頁渲染后的源代碼。
輸出 page_source 屬性即可。
這樣,我們就可以做到網(wǎng)頁的動(dòng)態(tài)爬取了。
測(cè)試用例
有了以上特性,我們當(dāng)然可以用來寫測(cè)試樣例了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
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.") 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)類的形式。
The test case class is inherited from unittest.TestCase. Inheriting from TestCase class is the way to tell unittest module that this is a test case. The setUp is part of initialization, this method will get called before every test function which you are going to write in this test case class. The test case method should always start with characters test. The tearDown method will get called after every test method. This is a place to do all cleanup actions. You can also call quit method instead of close. The quit will exit the entire browser, whereas close will close a tab, but if it is the only tab opened, by default most browser will exit entirely.
測(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)閉。
頁面操作
頁面交互
僅僅抓取頁面沒有多大卵用,我們真正要做的是做到和頁面交互,比如點(diǎn)擊,輸入等等。那么前提就是要找到頁面中的元素。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)容。
這樣輸入的文本會(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 Select select = 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ù)文字來選擇。是十分方便的。
全部取消選擇怎么辦呢?很簡單
|
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)是
如果你把表單都填好了,最后肯定要提交表單對(duì)吧。怎嗎提交呢?很簡單
|
driver.find_element_by_id("submit").click() |
這樣就相當(dāng)于模擬點(diǎn)擊了 submit 按鈕,做到表單提交。
當(dāng)然你也可以單獨(dú)提交某個(gè)元素
方法,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 的操作。
頁面切換
一個(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è)事件之后,頁面出現(xiàn)了彈窗提示,那么你怎樣來處理這個(gè)提示或者獲取提示信息呢?
|
alert = driver.switch_to_alert() |
通過上述方法可以獲取彈窗對(duì)象。
歷史記錄
那么怎樣來操作頁面的前進(jìn)和后退功能呢?
|
driver.forward() driver.back() |
嗯,簡潔明了。
Cookies處理
為頁面添加 Cookies,用法如下
|
# Go to the correct domain driver.get("http://www.") # Now set the cookie. This one's valid for the entire domain cookie = {‘name’ : ‘foo’, ‘value’ : ‘bar’} driver.add_cookie(cookie) |
獲取頁面 Cookies,用法如下
|
# Go to the correct domain driver.get("http://www.") # And now output all the available cookies for the current URL driver.get_cookies() |
以上便是 Cookies 的處理,同樣是非常簡單的。
元素選取
關(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ì)的元素選擇方法參見官方文檔
元素選擇
頁面等待
這是非常重要的一部分,現(xiàn)在的網(wǎng)頁越來越多采用了 Ajax 技術(shù),這樣程序便不能確定何時(shí)某個(gè)元素完全加載出來了。這會(huì)讓元素定位困難而且會(huì)提高產(chǎn)生 ElementNotVisibleException 的概率。
所以 Selenium 提供了兩種等待方式,一種是隱式等待,一種是顯式等待。
隱式等待是等待特定的時(shí)間,顯式等待是指定某一條件直到這個(gè)條件成立時(shí)繼續(xù)執(zhí)行。
顯式等待
顯式等待指定某個(gè)條件,然后設(shè)置最長等待時(shí)間。如果在這個(gè)時(shí)間還沒有找到元素,那么便會(huì)拋出異常了。
|
from selenium import webdriver from selenium.webdriver.common.by import By from 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.support import expected_conditions as EC wait = WebDriverWait(driver, 10) element = wait.until(EC.element_to_be_clickable((By.ID,'someid'))) |
隱式等待
隱式等待比較簡單,就是簡單地設(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ì)于頁面測(cè)試和分析,官方提供了一個(gè)比較明晰的代碼結(jié)構(gòu),可以參考。
頁面測(cè)試架構(gòu)
API
到最后,肯定是放松最全最重要的API了,比較多,希望大家可以多加練習(xí)。
API
結(jié)語
以上就是 Selenium 的基本用法,我們講解了頁面交互,頁面渲染之后的源代碼的獲取。這樣,即使頁面是 JS 渲染而成的,我們也可以手到擒來了。就是這么溜!
轉(zhuǎn)載請(qǐng)注明:靜覓 ? Python爬蟲利器五之Selenium的用法
|