天天看點

Selenium的使用

直接使用模拟浏覽器運作的方式來實作,這樣我們就可以做到浏覽器看到是什麼樣,抓取的源碼就是什麼樣,也就是可見即可爬。這樣我們就不用再去管網頁内部的 JavaScript 用了什麼算法渲染頁面,不用管網頁背景的 Ajax 接口到底有哪些參數,利用模拟浏覽器的方式我們都可以直接擷取 JavaScript 渲染的最終結果,隻要能在浏覽器中看到,我們都能抓取

1.環境配置

使用chrome浏覽器,下載下傳跟chrome浏覽器版本相比對的chromedriver.exe,放到目前使用的python環境的Scripts目錄下

然後在指令行裡執行chromedriver,出現如下畫面則說明配置成功

Selenium的使用

2.聲明浏覽器對象

Selenium 支援非常多的浏覽器,如 Chrome、Firefox、Edge 等,還有手機端的浏覽器 Android、BlackBerry 等,另外無界面浏覽器 PhantomJS 也同樣支援。

使用其他的浏覽器也需要做相應的環境配置才行。

可以用如下的方式初始化:

from selenium import webdriver

browser = webdriver.Chrome()      

 # browser = webdriver.Firefox()

# browser = webdriver.Edge()

# browser = webdriver.PhantomJS()

# browser = webdriver.Safari()

3.通路頁面

彈出Chrome 浏覽器,自動通路了淘寶,然後控制台輸出了淘寶頁面的源代碼,随後浏覽器關閉

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source)
browser.close()      

4.查找節點

Selenium 可以驅動浏覽器完成各種操作,比如填充表單、模拟點選等等,但是前提是需要知道節點所在的位置

4.1單個節點

所有擷取單個節點的方法:

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

通用find_element() 方法,它需要傳入兩個參數,一個是查找的方式 By,另一個就是值,實際上它就是 find_element_by_id() 這種方法的通用函數版本,比如 find_element_by_id(id) 就等價于 find_element(By.ID, id)

4.2多個節點

find_elements_by_id

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

也可以直接 find_elements() 方法來選擇,是以也可以這樣來寫:lis = browser.find_elements(By.CSS_SELECTOR, '.service-bd li')

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
lis = browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()      

5.節點互動

比較常見的用法有:

輸入文字用 send_keys() 方法,

清空文字用 clear() 方法,

按鈕點選用 click() 方法

官方文檔的互動動作介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement

import time

from selenium import webdriver

browser = webdriver.Chrome()
try:
    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()
finally:
    browser.close()      

6.動作鍊

還有另外一些操作它是沒有特定的執行對象的,比如滑鼠拖拽、鍵盤按鍵等操作。這些動作用另一種方式來執行,那就是動作鍊

更多的動作鍊操作可以參考官方文檔的動作鍊介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
try:
    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()
finally:
    browser.close()      

7.執行JavaScript

對于某些操作,Selenium API 是沒有提供的,如下拉進度條等,可以直接模拟運作 JavaScript,使用 execute_script() 方法即可實作

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')      

利用了 execute_script() 方法将進度條下拉到最底部,然後彈出 alert 提示框

8.擷取節點資訊

8.1擷取屬性

使用 get_attribute() 方法來擷取節點的屬性,那麼這個的前提就是先選中這個節點

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
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('href'))      

8.2擷取文本值

每個 WebEelement 節點都有 text 屬性,我們可以通過直接調用這個屬性就可以得到節點内部的文本資訊了,就相當于 BeautifulSoup 的 get_text() 方法、PyQuery 的 text() 方法

from selenium import webdriver

browser = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
browser.get(url)
input = browser.find_element_by_class_name('zu-top-add-question')
print(input.text)      

8.3.擷取ID、位置、标簽名、大小

from selenium import webdriver

browser = webdriver.Chrome()
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)      

9.切換Frame

在網頁中有這樣一種節點叫做 iframe,也就是子Frame,相當于頁面的子頁面,它的結構和外部網頁的結構是完全一緻的。

Selenium 打開頁面後,它預設是在父級Frame 裡面操作,而此時如果頁面中還有子 Frame,它是不能擷取到子 Frame 裡面的節點的。是以這時候我們就需要使用 switch_to.frame() 方法來切換 Frame。

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

browser = webdriver.Chrome()
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)      

我們先通過 switch_to.frame() 方法切換到子 Frame 裡面,然後我們嘗試擷取父級 Frame 裡的 LOGO 節點,是不能找到的,找不到的話就會抛出 NoSuchElementException 異常,異常被捕捉之後就會輸出 NO LOGO,接下來我們重新切換回父Frame,然後再次重新擷取節點,發現就可以成功擷取了。

是以說當頁面中包含子 Frame 時,如果我們想擷取子Frame 中的節點,需要先調用 switch_to.frame() 方法切換到對應的 Frame,然後再進行操作即可

10.延時等待

在 Selenium 中,get() 方法會在網頁架構加載結束之後就結束執行,此時如果擷取 page_source 可能并不是浏覽器完全加載完成的頁面,如果某些頁面有額外的 Ajax 請求,我們在網頁源代碼中也不一定能成功擷取到。是以這裡我們需要延時等待一定時間確定節點已經加載出來。

在這裡等待的方式有兩種,一種隐式等待,一種顯式等待

10.1 隐式等待

如果 Selenium 沒有在DOM 中找到節點,将繼續等待,超出設定時間後則抛出找不到節點的異常, 預設的時間是 0

用 implicitly_wait() 方法實作了隐式等待

from selenium import webdriver

browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)      

10.2 顯式等待

指定好要查找的節點,然後指定一個最長等待時間。如果在規定時間内加載出來了這個節點,那就傳回查找的節點,如果到了規定時間依然沒有加載出該節點,則會抛出逾時異常。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser, 0.01)
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_conditions,比如在這裡我們傳入了 presence_of_element_located 這個條件,就代表節點出現的意思,其參數是節點的定位元組,也就是 ID 為 q 的節點搜尋框。是以這樣可以做到的效果就是,在 10 秒内如果 ID 為 q 的節點即搜尋框成功加載出來了,那就傳回該節點,如果超過10 秒還沒有加載出來,那就抛出異常。

對于按鈕,可以更改一下等待條件,比如改為 element_to_be_clickable,也就是可點選,是以查找按鈕時是查找 CSS 選擇器為 .btn-search 的按鈕,如果 10 秒内它是可點選的也就是成功加載出來了,那就傳回這個按鈕節點,如果超過 10 秒還不可點選,也就是沒有加載出來,那就抛出異常。

所有的加載條件:

等待條件 含義
title_is 标題是某内容
title_contains 标題包含某内容
presence_of_element_located 節點加載出,傳入定位元組,如(By.ID, 'p')
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 frame 加載并切換
invisibility_of_element_located 節點不可見
element_to_be_clickable 節點可點選
staleness_of 判斷一個節點是否仍在DOM,可判斷頁面是否已經重新整理
element_to_be_selected 節點可選擇,傳節點對象
element_located_to_be_selected 節點可選擇,傳入定位元組
element_selection_state_to_be 傳入節點對象以及狀态,相等傳回True,否則傳回False
element_located_selection_state_to_be 傳入定位元組以及狀态,相等傳回True,否則傳回False
alert_is_present 是否出現Alert

更多詳細的等待條件的參數及用法介紹可以參考官方文檔:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions

11.前進後退

在Selenium中使用 back() 方法可以後退,forward() 方法可以前進

import time

from selenium import webdriver

browser = webdriver.Chrome()
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()      

連續通路三個頁面,然後調用 back() 方法就可以回到第二個頁面,接下來再調用 forward() 方法又可以前進到第三個頁面

12.cookies

使用 Selenium 還可以友善地對 Cookies 進行操作,例如擷取、添加、删除 Cookies 等等

from selenium import webdriver

browser = webdriver.Chrome()
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,再重新擷取,結果就為空了。

13.頁籤管理

import time

from selenium import webdriver

browser = webdriver.Chrome()
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')      

我們通路了百度,然後調用了 execute_script() 方法,傳入 window.open() 的 JavaScript 語句新開啟一個頁籤,然後接下來我們想切換到該頁籤,可以調用 window_handles 屬性擷取目前開啟的所有頁籤,傳回的是頁籤的代号清單,要想切換頁籤隻需要調用 switch_to.window() 方法,傳入頁籤的代号即可。在這裡我們将第二個頁籤代号傳入,即跳轉到了第二個頁籤,然後接下來在第二個頁籤下打開一個新的頁面,然後切換回第一個頁籤可以重新調用 switch_to.window() 方法,再執行其他操作即可

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException

browser = webdriver.Chrome()
try:
    browser.get('https://www.baidu.com')
except TimeoutException:
    print('Time Out')
try:
    browser.find_element_by_id('hello')
except NoSuchElementException:
    print('No Element')
finally:
    browser.close()