python爬蟲入門教程之 Selenium庫(kù)的使用
上一節(jié)我們講解了PhantomJS的用法,它只是一個(gè)沒有界面的瀏覽器,運(yùn)行的還是JavaScript腳本,這和Python爬蟲開發(fā)有什么聯(lián)系呢?本節(jié)介紹的Selenium能將Python和PhantomJS緊密地聯(lián)系起來,從而實(shí)現(xiàn)爬蟲的開發(fā)。
Selenium是一個(gè)自動(dòng)化測(cè)試工具,支持各種瀏覽器,包括Chrome、Safari、Firefox等主流界面式瀏覽器,也包括PhantomJS等無界面瀏覽器,通俗來說Selenium支持瀏覽器驅(qū)動(dòng),可以對(duì)瀏覽器進(jìn)行控制。而且Selenium支持多種語(yǔ)言開發(fā),比如Java、C、Ruby,還有Python,因此Python+Selenium+PhantomJS的組合就誕生了。
PhantomJS負(fù)責(zé)渲染解析JavaScript,Selenium負(fù)責(zé)驅(qū)動(dòng)瀏覽器和與Python對(duì)接,Python負(fù)責(zé)做后期處理,三者構(gòu)成了一個(gè)完整的爬蟲結(jié)構(gòu)。
9.4.1 安裝Selenium
Selenium現(xiàn)在最新的版本為3.0.1,本書也是以此為標(biāo)準(zhǔn)進(jìn)行講解。Selenium官方地址為:http://www.seleniumhq.org/,其安裝主要有兩種方式:
·pip install Selenium==3.0.1·從https://pypi.python.org/pypi/selenium下載源代碼解壓后,運(yùn)行python setup.py install。
·Selenium3.x和Selenium2.x版本有以下區(qū)別:
·Selenium2.x調(diào)用高版本瀏覽器會(huì)出現(xiàn)不兼容問題,調(diào)用低版本瀏覽器正常。
·Selenium3.x調(diào)用瀏覽器必須下載一個(gè)類似補(bǔ)丁的文件,比如Firefox的為geckodriver,Chrome的為chromedriver。
各種版本瀏覽器的補(bǔ)丁下載地址為:http://www.seleniumhq.org/download/,如圖9-11所示。
圖9-11 補(bǔ)丁下載地址
根據(jù)自己的操作系統(tǒng),下載指定的geckodriver文件。下面以Firefox為例,對(duì)geckodriver進(jìn)行配置。在ubuntu下,將文件下載下來之后解壓到指定目錄下,我把它解壓到firefoxDriver目錄下,如圖9-12所示。
圖9-12 補(bǔ)丁解壓位置
接著配置環(huán)境變量,在shell中執(zhí)行:export PATH=$PATH:/home/ubuntu/firefoxDr-iver,將geckodriver所在的目錄配置到環(huán)境變量中,其他操作系統(tǒng)配置方式類似。
9.4.2 Selenium快速入門
安裝和配置完成后,現(xiàn)在開始使用Selenium寫一個(gè)小例子,功能是打開百度主頁(yè),在搜索框中輸入網(wǎng)絡(luò)爬蟲,進(jìn)行搜索。代碼如下:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
assert u"百度" in driver.title
elem = driver.find_element_by_name("wd")
elem.clear()
elem.send_keys(u"網(wǎng)絡(luò)爬蟲")
elem.send_keys(Keys.RETURN)
time.sleep(3)
assert u"網(wǎng)絡(luò)爬蟲." not in driver.page_source
driver.close()
效果如圖9-13所示。
圖9-13 搜索網(wǎng)絡(luò)爬蟲
代碼分析:首先使用webdriver.Firefox()獲取Firefox瀏覽器的驅(qū)動(dòng),調(diào)用get方法,打開百度首頁(yè),判斷標(biāo)題中是否包含百度字樣,接著通過元素名稱wd獲取輸入框,通過send_keys方法將網(wǎng)絡(luò)爬蟲填寫其中,然后回車。延時(shí)3秒后,判斷搜索頁(yè)面是否有網(wǎng)絡(luò)爬蟲字樣,最后關(guān)閉driver。
我相信即使是同樣的代碼,大家也會(huì)遇到各種各樣的問題。下面將大家可能遇到的問題進(jìn)行一下總結(jié):
1)錯(cuò)誤信息為:Exception AttributeError:“’Service‘object has no attribute’process‘”in...,可能是geckodriver環(huán)境變量有問題,重新將geckodriver所在目錄配置到環(huán)境變量中。或者直接在代碼中指定路徑:
webdriver.Firefox(executable_path=’/home/ubuntu/firefoxDriver/geckodriver)‘
2)錯(cuò)誤信息為:selenium.common.exceptions.
WebDriverException:Message:Unsupported Marionette protocol
version 2,required 3,可能是Firefox版本太低,使用Selenium3.x要求Firefox>=v47。
3)錯(cuò)誤信息為:selenium.common.exceptions.
WebDriverException:Message:Failed to start browser,可能是沒找到Firefox瀏覽器,可以在代碼中指定Firefox的位置:
binary = FirefoxBinary(r'E:\Mozilla Firefox\firefox.exe')
driver = webdriver.Firefox(firefox_binary=binary)
9.4.3 元素選取
要想對(duì)頁(yè)面進(jìn)行操作,首先要做的是選中頁(yè)面元素。元素選取方法如表9-5所示。
表9-5 定位方法
除了上面具有確定功能的方法,還有兩個(gè)通用方法find_element和find_elements,可以通過傳入?yún)?shù)來指定功能。示例如下:
from selenium.webdriver.common.by import By
driver.find_element(By.XPATH, '// button[text()="Some text"]')
這一個(gè)例子是通過xpath表達(dá)式來查找,方法中第一個(gè)參數(shù)是指定選取元素的方式,第二個(gè)參數(shù)是選取元素需要傳入的值或表達(dá)式。
第一個(gè)參數(shù)還可以傳入By類中的以下值:
·By.ID·By.XPATH·By.LINK_TEXT·By.PARTIAL_LINK_TEXT·By.NAME
·By.TAG_NAME·By.CLASS_NAME·By.CSS_SELECTOR
下面通過一個(gè)HTML文檔來講解一下如何使用以上方法提取內(nèi)容,HTML文檔如下:
<html> <body> <h1>Welcome</h1> <p class="content">用戶登錄</p> <form id="loginForm"> <input name="username" type="text" /> <input name="password" type="password" /> <input name="continue" type="submit" value="Login" /> <input name="continue" type="button" value="Clear" /> </form> <a href="register.html">Register</a> </body> <html>
定位方法的使用如表9-6所示。
表9-6 定位方法示例
9.4.4 頁(yè)面操作
以如下HTML文檔為例介紹頁(yè)面操作,login.html代碼如下:
<html> <head> <meta http-equiv="content-type" content="text/html;charset=gbk"> </head> <body> <h1>Welcome</h1> <p class="content">用戶登錄</p> <form id="loginForm"> <select name="loginways"> <option value="email">郵箱</option> <option value="mobile">手機(jī)號(hào)</option> <option value="name">用戶名</option> </select> <br/> <input name="username" type="text" />
<br/> 密碼
<br/> <input name="password" type="password" /> <br/><br/> <input name="continue" type="submit" value="Login" /> <input name="continue" type="button" value="Clear" /> </form> <a href="register.html">Register</a> </body> </html>
效果如圖9-14所示。
圖9-14 登錄頁(yè)面
1.頁(yè)面交互與填充表單
第一步:初始化Firefox驅(qū)動(dòng),打開html文件,由于是本地文件,可以使用下面方式打開。
driver = webdriver.Firefox()
driver.get("file:// /e:/login.html")
第二步:獲取用戶名和密碼的輸入框,和登錄按鈕。
username = driver.find_element_by_name('username')
password = driver.find_element_by_xpath(".// *[@id='loginForm']/input[2]")
login_button = driver.find_element_by_xpath("// input[@type='submit']")
第三步:使用send_keys方法輸入用戶名和密碼,使用click方法模擬點(diǎn)擊登錄。
username.send_keys("qiye")
password.send_keys("qiye_pass")
login_button.click()
如果想清除username和password輸入框的內(nèi)容,可以使用clear方法。
username.clear()
password.clear()
上面還有一個(gè)問題沒解決,如何操作下拉選項(xiàng)卡選擇登錄方式呢?第一種方法代碼如下:
select = driver.find_element_by_xpath("// form/select")
all_options = select.find_elements_by_tag_name("option")
for option in all_options:
print("Value is: %s" % option.get_attribute("value"))
option.click()
在代碼中首先獲取select元素,也就是下拉選項(xiàng)卡。然后輪流設(shè)置了select選項(xiàng)卡中的每一個(gè)option選項(xiàng)。這并不是一個(gè)非常好的辦法。官方提供了更好的實(shí)現(xiàn)方式,在WebDriver中提供了一個(gè)叫
Select方法,也就是第二種操作方式。代碼如下:
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_xpath('// form/select '))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)
它可以根據(jù)索引、文字、value值來選擇選項(xiàng)卡中的某一項(xiàng)。
如果select標(biāo)記中multiple=“multiple”,也就是說這個(gè)select標(biāo)記支持多選,Select對(duì)象提供了支持此功能的方法和屬性。示例如下:
·取消所有的選項(xiàng):select.deselect_all()
·獲取所有的選項(xiàng):select.options·獲取已選中的選項(xiàng):select.all_selected_options
2.元素拖拽
元素的拖拽即將一個(gè)元素拖到另一個(gè)元素的位置,類似于拼圖。
首先要找到源元素和目的元素,然后使用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()
3.窗口和頁(yè)面frame的切換
一個(gè)瀏覽器一般都會(huì)開多個(gè)窗口,我們可以switch_to_window方法實(shí)現(xiàn)指定窗口的切換。示例如下:
driver.switch_to_window("windowName")
也可以通過window handle來獲取每個(gè)窗口的操作對(duì)象。示例如下:
for handle in driver.window_handles:
driver.switch_to_window(handle)
如需切換頁(yè)面frame,可以使用switch_to_frame方法,示例如下:
driver.switch_to_frame("frameName")
driver.switch_to_frame("frameName.0.child")
4.彈窗處理
如果你在處理頁(yè)面的過程中,觸發(fā)了某個(gè)事件,跳出彈框。可以使用switch_to_alert獲取彈框?qū)ο?,從而進(jìn)行關(guān)閉彈框、獲取彈框信息等操作。示例如下:
alert = driver.switch_to_alert()
alert.dismiss()
5.歷史記錄
操作頁(yè)面的前進(jìn)和后退功能,示例如下:
driver.forward()
driver.back()
6.Cookie處理
可以使用get_cookies方法獲取cookie,也可以使用add_cookie方法添加cookie信息。示例如下:
driver.get("http://www.baidu.com")
cookie = {'name': 'foo', 'value' : 'bar'} driver.add_cookie(cookie)
driver.get_cookies()
7.設(shè)置phantomJS請(qǐng)求頭中User-Agent
這個(gè)功能在爬蟲中非常有用,一般針對(duì)phantomJS的反爬蟲措施都會(huì)檢測(cè)這個(gè)字段,默認(rèn)的User-Agent中含有phantomJS內(nèi)容,可以通過代碼進(jìn)行修改。代碼如下:
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = ( "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.
36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36"
)
driver = webdriver.PhantomJS()# desired_capabilities=dcap)
driver.get("http://www.google.com")
driver.quit()
9.4.5 等待
由于現(xiàn)在很多網(wǎng)站采用Ajax技術(shù),不確定網(wǎng)頁(yè)元素什么時(shí)候能被完全加載,所以網(wǎng)頁(yè)元素的選取會(huì)比較困難,這時(shí)候就需要等待。
Selenium有兩種等待方式,一種是顯式等待,一種是隱式等待。
1.顯式等待
顯式等待是一種條件觸發(fā)式的等待方式,指定某一條件直到這個(gè)條件成立時(shí)才會(huì)繼續(xù)執(zhí)行,可以設(shè)置超時(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.Firefox()
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()
以上代碼加載http://somedomain/url_that_delays_loading頁(yè)面,并定位id為myDynamic Element的元素,設(shè)置超時(shí)時(shí)間為10s。
WebDriverWait默認(rèn)會(huì)500ms檢測(cè)一下元素是否存在。
Selenium提供了一些內(nèi)置的用于顯式等待的方法,位于
expected_conditions類中,方法名稱如表9-7所示:
表9-7 內(nèi)置方法
2.隱式等待
隱式等待是在嘗試發(fā)現(xiàn)某個(gè)元素的時(shí)候,如果沒能立刻發(fā)現(xiàn),就等待固定長(zhǎng)度的時(shí)間,類似于socket超時(shí),默認(rèn)設(shè)置是0秒。一旦設(shè)置了隱式等待時(shí)間,它的作用范圍是Webdriver對(duì)象實(shí)例的整個(gè)生命周期,也就是說Webdriver執(zhí)行每條命令的超時(shí)時(shí)間都是如此。如果大家感覺設(shè)置的時(shí)間過長(zhǎng),可以進(jìn)行不斷地修改。使用方法示例如下:
from selenium import webdriver
driver = webdriver.Firefox()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
3.線程休眠
time.sleep(time),這是使用線程休眠延時(shí)的辦法,也是比較常用的。
如對(duì)本文有疑問,請(qǐng)?zhí)峤坏浇涣髡搲瑥V大熱心網(wǎng)友會(huì)為你解答??! 點(diǎn)擊進(jìn)入論壇