直接使用模拟浏覽器運作的方式來實作,這樣我們就可以做到浏覽器看到是什麼樣,抓取的源碼就是什麼樣,也就是可見即可爬。這樣我們就不用再去管網頁内部的 JavaScript 用了什麼算法渲染頁面,不用管網頁背景的 Ajax 接口到底有哪些參數,利用模拟浏覽器的方式我們都可以直接擷取 JavaScript 渲染的最終結果,隻要能在浏覽器中看到,我們都能抓取
1.環境配置
使用chrome浏覽器,下載下傳跟chrome浏覽器版本相比對的chromedriver.exe,放到目前使用的python環境的Scripts目錄下
然後在指令行裡執行chromedriver,出現如下畫面則說明配置成功

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()