昨天搞了一天搞這個谷歌驅動,還是沒成功到最後還是選擇了用狐火來進行selenium的使用首先我們使用的環境是FireFox47和python3.6,驅動位址連結:https://pan.baidu.com/s/1THxUan49YlKOwSTpFpshWg 密碼:ibcw下載下傳好後放置在python的script的目錄下,不知道怎麼回事我用谷歌浏覽器一直就是使用不起來,昨天搞了一天了,百度說是驅動問題,但我驅動和浏覽器的版本都是對應的,不存在驅動問題的。
言歸正傳,Selenium是一個自動化測試工具,利用它可以驅動浏覽器執行特定的動作,如點選、下拉等操作,同時還可以擷取浏覽器目前呈現的頁面的源代碼,做到可見即可爬。
一.聲明浏覽器對象
Selenium支援非常多的浏覽器,如Chrome、FireFox、Edge等我們用如下方式進行初始化:
from selenium import webdriver
browser=webdriver.Chrome()
browser=webdriver.Firefox()
browser=webdriver.Edge()
這樣就完成了浏覽器對象的初始化并将其指派為browser對象。接下來,我們要做的就是調用browser對象,讓其執行各個動作以模拟浏覽器操作。
二.通路頁面
我們可以用get()方法請求網頁,參數傳傳入連結接URL即可。如下所示:
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
print(browser.page_source) #列印輸出網頁源代碼
browser.close()
通過這幾行簡單的代碼,我們可以實作浏覽器的驅動并擷取網頁源碼,非常便捷。
三.查找節點
Selenium可以驅動浏覽器完成各種操作,比如填充表單、模拟點選等。比如,我們想要完成向某個輸入框輸入文字操作,總需要知道這個輸入框的位置,而selenium提供一系列查找節點的方法
單個節點
我們可以用find_element_by_name()根據那麼值擷取,find_element_by_id()是根據id擷取。另外,還有根據XPath、CSS選擇器等擷取方式。
如下所示:
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
input_first=browser.find_element_by_id('q')
input_second=browser.find_element_by_css_selector('#q')
input_third=browser.find_element_by_xpath('//*[@id="q"]')
print(input_first,input_second,input_third)
browser.close()
這裡我們使用3種方式擷取輸入框,分别是ID,CSS選擇器和XPath擷取,它們傳回的結果完全一緻。運作結果如下:
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")>
可以看到,這3個節點都是WebElement類型,是完全一緻的。
這裡列出所有擷取單個節點的方法:
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
另外,Selenium還提供了通用方法find_element(),它需要傳入兩個參數:查找方式By和值。實際上,它就是find_element_by_id()這種方法的通用函數版本,比如find_element_by_id(id)就等價于find_element(By.ID,id),二者得到的結果完全一緻。我們用代碼實作下:
from selenium import webdriver
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input_first = browser.find_element(By.ID, 'q')
print(input_first)
browser.close()
多個節點
如果查找的目标在網頁上隻有一個,那麼完全可以用find_element()方法。但如果有多個節點,再用find_element()方法查找,就隻能得到第一個節點了。如果要查找所有滿足條件的節點,需要用find_elements()這樣的方法。如下所示:
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
lis=browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()
運作結果如下:
from selenium import webdriver
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
input=browser.find_element_by_class_name('zu-top-add-question')
print(input.text)
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="860ee34c-7bde-4dd0-9487-11657b6cf669", element="4678c2d7-4e29-48e5-a867-704cb3c1d869")>
可以看到,得到的内容變成了清單類型,清單中的每個節點都是WebElement類型。
也就是說,如果我們用find_element()方法,隻能擷取比對的第一個節點,結點是WebElement類型。如果用find_elements()方法,則結果是清單類型,清單中的每個節點是WebElement類型。
這裡列出所有擷取多個節點的方法:
find_elementS_by_id
find_elementS_by_name
find_elementS_by_xpath
find_element_by_link_text
find_elementS_by_partial_link_text
find_elementS_by_tag_name
find_elementS_by_class_name
find_elementS_by_css_selector
當然,我們也可以直接用find_elements()方法來選擇,這時可以這樣寫:
lis=browser.find_elements(By.CSS_SELECTOR,'.service-bd li')
結果是完全一緻的。
節點互動
Selenium可以驅動浏覽器來執行一些操作,也就是說可以讓浏覽器模拟執行一些動作。比較常見的用法有:輸入文字時用send_keys()方法,清空文字用clear()方法,點選按鈕時用click()方法,示例如下:
from selenium import webdriver
import time
browser=webdriver.Firefox()
browser.get('https://www.taobao.com')
input=browser.find_element_by_id('q')
input.send_keys('iPhone')
time.sleep(1)
input.clear()
input.send_keys('iPad')
button=browser.find_element_by_class_name('btn-search')
button.click()
這裡首先驅動浏覽器打開淘寶,然後用find_element_by_id()方法擷取輸入框,然後用send_keys()方法輸入iPhone文字,等待一秒後用clear()方法清空輸入框,再次調用send_keys()方法輸入iPad文字,之後再用find_element_by_class_name()方法擷取搜尋按鈕,最後調用click()方法完成搜尋動作。
動作鍊
在上面的執行個體中,一些互動動作都是針對某個節點執行的。比如,對于輸入框,我們就調用它的輸入文字和清空文字方法;對于按鈕,就調用它的點選方法。其實,還有另外一些操作,它們沒有特定的執行對象,這些動作用另一種方式來執行,那就是動作鍊。
from selenium import webdriver
from selenium.webdriver import ActionChains
browser = webdriver.Firefox()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element_by_css_selector('#draggable')
target = browser.find_element_by_css_selector('#droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source, target)
actions.perform()
首先我們打開網頁中的一個拖拽執行個體,然後依次選中要被拖拽的節點和拖拽到的目标節點,然後聲明了 ActionChains 對象指派為 actions 變量,然後通過調用 actions 變量的 drag_and_drop() 方法,然後再調用 perform() 方法執行動作,就完成了拖拽操作.
執行JavaScript
我們用execut_script()方法實作,如下所示:
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')
這裡我們利用excute_script()方法将進度條下拉到最底部,然後彈出alert提示框
擷取節點資訊
selenium提供了選擇節點的方法,傳回的是WebElement類型,那麼它也有相關的方法和屬性來直接提取節點資訊,如文本、屬性等。這樣的話,我們就可以不通過解析源代碼來提取資訊。
- 擷取屬性
我們可以使用get_attribute()方法來擷取節點的屬性,但是前提是先選中這個節點,示例如下:
from selenium import webdriver
from selenium.webdriver import ActionChains
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
logo=browser.find_element_by_id('zh-top-link-logo')
print(logo)
print(logo.get_attribute('class'))
運作之後,程式便會用火狐浏覽器打開知乎頁面,然後擷取知乎的logo節點,最後列印出它的class。
輸出結果如下
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="c3095667-b9d9-4100-8538-047d3d97a247", element="f286250c-48eb-4189-bf7b-d993e3339ce0")>
zu-top-link-logo
通過get_attribute()方法,然後傳入想要擷取的屬性名,就可以得到它的值了。
- 擷取文本值
每個WebElement節點都有text屬性,直接調用這個屬性就可以得到節點内部的文本資訊,這相當于Beautiful Soup的get_text()方法,如下所示:
from selenium import webdriver
browser=webdriver.Firefox()
url='https://www.zhihu.com/explore'
browser.get(url)
input=browser.find_element_by_class_name('zu-top-add-question')
print(input.text)
這裡擷取的“提問”這個節點,再将其文本值列印出來。
控制台輸出結果如下:
提問
- 擷取id、位置、标簽名和大小
另外,WebElement節點還有其他一些屬性,比如id屬性可以擷取id,location屬性可以擷取該節點在頁面中的相對位置,tag_name屬性可以擷取标簽名稱,size屬性可以擷取節點的大小,也就是寬高,如下所示:
from selenium import webdriver
browser = webdriver.Firefox()
url = 'https://www.zhihu.com/explore'
browser.get(url)
input = browser.find_element_by_class_name('zu-top-add-question')
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)
這裡首先獲得"提問“按鈕這個節點,然後調用其id、location、tag_name、size屬性來擷取對應的屬性值。
切換Frame
我們知道網頁中有一種節點叫做iframe,也就是子Frame,相當于頁面的子頁面,它的結構和外部網頁的結構完全一緻。Selenium打開網頁後,它預設是在父級節點Frame裡面操作,而此時如果頁面中還有子Frame,它是不能擷取到子Frame裡面的節點的。這時需要使用switch_to,.frame()方法來切換Frame。示例如下:
import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
browser=webdriver.Firefox()
url='http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
try:
logo=browser.find_element_by_class_name('logo')
except NoSuchElementException:
print('NO LOGO')
browser.switch_to.parent_frame()
logo=browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)
輸出如下:
NO LOGO
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="f8e8a398-fb94-4dfd-89b6-4fe3bfb50c52", element="29b03253-8b92-4187-bb44-388aecb60f45")>
RUNOOB.COM
這裡首先通過switc_to.frame()方法切換到子Frame裡面,然後嘗試擷取父級Frame裡的logo節點(這裡是不可能找到的),如果找不到的話,就會抛出NoSuchElementException異常,異常被捕捉之後,就會輸出NO LOGO。接下來,重新切換到父級Frame,然後再次重新擷取節點,發現此時可以成功擷取了。
switch_to.frame()方法是當頁面中包含子Frame時,擷取子Frame中的節點。
延時等待
- 隐式等待
當使用隐式等待執行測試的時候,如果Selenium沒有在DOM中找到節點,将繼續等待,超出設定時間後,則抛出找不到節點的異常。如下所示:
from selenium import webdriver
browser = webdriver.Firefox()
browser.implicitly_wait(10)
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)
- 顯示等待
顯示等待它指定要查找的節點,然後指定一個最長等待時間。如果在規定時間内加載出來這個節點,就傳回查找的節點;如果到了規定的時間依然沒有加載出該節點,則抛出異常,如下所示:
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
browser=webdriver.Firefox()
browser.get('https://www.taobao.com/')
wait=WebDriverWait(browser,10)
input=wait.until(EC.presence_of_element_located((By.ID,'q')))
button=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))
print(input,button)
這裡首先引入WebDriverWait這個對象,指定最長等待時間,然後調用它的until()方法,傳入要等待條件expected_condititions。比如,這裡傳入了presence_of_element_located這個條件,代表節點出現的意思。其參數是節點的定位元祖,也就是ID為q的節點搜尋框。
這樣可以做到的效果就是,在10秒内如果ID為q的節點成功加載出來,就傳回該節點;如果超過10秒還沒加載出來,就抛出異常
對于按鈕,可以更改一下等待條件,比如改為 element_to_be_clickable,也就是可點選,是以查找按鈕時是查找 CSS 選擇器為 .btn-search 的按鈕,如果 10 秒内它是可點選的也就是成功加載出來了,那就傳回這個按鈕節點,如果超過 10 秒還不可點選,也就是沒有加載出來,那就抛出異常。
運作代碼,在網速較佳的情況下是可以成功加載出來的。
控制台輸出:
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="a9445dae-1106-4c33-9f88-7aa757587287", element="b539ab53-bbbc-4018-8e75-9eb179b28b80")> <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="a9445dae-1106-4c33-9f88-7aa757587287", element="4356a278-4651-4d7f-9769-c6e97f1a5cdd")>
關于等待條件,其實還有很多,具體百度有。
前進和後退
Selenium中提供了back()方法後退,使用forward()方法前進。如下所示:
import time
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.baidu.com/')
browser.get('https://www.taobao.com/')
browser.get('https://www.python.org/')
browser.back()
time.sleep(1)
browser.forward()
browser.close()
這裡我們連續通路3個頁面,然後調用back()方法回到第二個頁面,接下來再調用forward()方法又可以前進到第三個頁面。
Cookies
使用Selenium,還可以友善地對Cookies進行操作,例如擷取、添加、删除Cookies等。示例如下:
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germey'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
首先我們通路了知乎,然後加載完成之後,浏覽器實際上已經生成了 Cookies 了,我們調用 get_cookies() 方法就可以擷取所有的 Cookies,然後我們添加一個 Cookie,傳入一個字典,有 name、domain、value 等内容。接下來我們再次擷取所有的 Cookies,可以發現結果就多了這一項 Cookie。最後我們調用 delete_all_cookies() 方法,删除所有的 Cookies,再重新擷取,結果就為空了。
輸出結果如下:
[{'name': 'tgw_l7_route', 'value': '200d77f3369d188920b797ddf09ec8d1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'q_c1', 'value': '0a04eaa0d8794c92950f448476254113|1532313479000|1532313479000', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_xsrf', 'value': '5c49b528447609a4fdb0a69e347d2fe1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'r_cap_id', 'value': '"ZmJlNzYxMDk0OGQ5NDY2ODgxOWQ5NGUzZWJlNGY2NDU=|1532313479|f2d05a38cae52a8a465c107214fd78355ac08326"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'cap_id', 'value': '"MTdmZDEyMGE1MDM5NDZhZmJjMWNiYjYyZDE2ZGUxNGM=|1532313479|b564e56d6e509522c20b4934bef527223d6d7a20"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_cap_id', 'value': '"YjA4YmEyYjg2NjY5NGQ1NDk2N2FhYjg5ZDY4MWU4OTQ=|1532313479|24b4d2925e30855d55ea74c047406d614a5be5e8"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'd_c0', 'value': '"AFAmMLhT8Q2PTjXr7t-dcrv3usuUs8WokLE=|1532313482"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_zap', 'value': '8399c18a-f9ea-4bc7-bc88-2e1da0aef956', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utma', 'value': '51854390.321058288.1532313485.1532313485.1532313485.1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmb', 'value': '51854390.0.10.1532313485', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmc', 'value': '51854390', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmz', 'value': '51854390.1532313485.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmv', 'value': '51854390.000--|3=entry_date=20180723=1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}]
[{'name': 'tgw_l7_route', 'value': '200d77f3369d188920b797ddf09ec8d1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'q_c1', 'value': '0a04eaa0d8794c92950f448476254113|1532313479000|1532313479000', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_xsrf', 'value': '5c49b528447609a4fdb0a69e347d2fe1', 'path': '/', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'r_cap_id', 'value': '"ZmJlNzYxMDk0OGQ5NDY2ODgxOWQ5NGUzZWJlNGY2NDU=|1532313479|f2d05a38cae52a8a465c107214fd78355ac08326"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'cap_id', 'value': '"MTdmZDEyMGE1MDM5NDZhZmJjMWNiYjYyZDE2ZGUxNGM=|1532313479|b564e56d6e509522c20b4934bef527223d6d7a20"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'l_cap_id', 'value': '"YjA4YmEyYjg2NjY5NGQ1NDk2N2FhYjg5ZDY4MWU4OTQ=|1532313479|24b4d2925e30855d55ea74c047406d614a5be5e8"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'n_c', 'value': '1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'd_c0', 'value': '"AFAmMLhT8Q2PTjXr7t-dcrv3usuUs8WokLE=|1532313482"', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '_zap', 'value': '8399c18a-f9ea-4bc7-bc88-2e1da0aef956', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utma', 'value': '51854390.321058288.1532313485.1532313485.1532313485.1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmb', 'value': '51854390.0.10.1532313485', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmc', 'value': '51854390', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmz', 'value': '51854390.1532313485.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': '__utmv', 'value': '51854390.000--|3=entry_date=20180723=1', 'path': '/', 'domain': '.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}, {'name': 'name', 'value': 'germey', 'path': '', 'domain': 'www.zhihu.com', 'expiry': None, 'secure': False, 'httpOnly': False}]
[]
頁籤管理
import time
from selenium import webdriver
browser=webdriver.Firefox()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(1)
browser.switch_to_window(browser.window_handles[0])
browser.get('https://python.org')