Class 17 - 1 动态渲染页面爬取 — Selenium使用
利用Selenium 可以驅動瀏覽器執行特定的動作,如點擊、下拉等操作, 同時還可以獲取瀏覽器當前呈現的頁面的源代碼 ,做到可見即可爬。
- 示例: from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
browser = webdriver.Chrome()
try:browser.get('https://www.baidu.com')input = browser.find_element_by_id('kw')input.send_keys('Python')input.send_keys(Keys.ENTER)wait = WebDriverWait(browser,10)wait.until(EC.presence_of_element_located((By.ID,'content_left')))print(browser.current_url)print(browser.get_cookies())print(browser.page_source)
finally:browser.close()
運行代碼后,會自動彈出一個 Chrome 瀏覽器。首先會跳轉到百度,然后在搜索框中輸入 Python,接著跳轉到搜索頁。??搜索結果加載出來后,控制臺分別會輸出當前的 URL 、當前的 Cookies 和網頁源代碼.
-
如果用 Selenium 來驅動瀏覽器加載網頁的話,就可以直接拿到 JavaScript 渲染的結果,不用擔心使用的是什么加密系統。聲明瀏覽器對象
- Selenium 支持非常多瀏覽器,如 Chrome/ Firefox/ Edge等,還有 Android 等手機端的瀏覽器,也支持無界面瀏覽器 PhantomJS。
- 初始化方式: from selenium import webdriver
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.PhantomJS()
browser = webdriver.Safari()
瀏覽器對象的初始化并將其賦值為 browser對象。接下來,就是調用 browser 對象,讓其執行各個動作以模擬瀏覽器操作
- 初始化方式: from selenium import webdriver
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.PhantomJS()
browser = webdriver.Safari()
- 用 get()方法來請求網頁,參數傳入鏈接 URL 即可。如用 get()方法訪問淘寶, 然后輸出源代碼,代碼: from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
print(browser.page_source)
彈出了 Chrome 瀏覽器并且自動訪問了淘寶,然后控制臺輸出了淘寶頁面的源代碼, 隨后瀏覽器關閉。可以實現瀏覽器的驅動并獲取網頁源碼
- Selenium 可以驅動瀏覽器完成各種操作,如填充表單、模擬點擊等。Selenium 提供了系列查找節點的方法,我們可以用這些方法來獲取想要的節點,以便執行一些動作或者提取信息。
- 如,要從淘寶頁面中提取搜索框這個節點,觀察源代碼(它的 id 是 q, name 也是q。此時就可以用多種方式獲取它。如,find_element_by_name()是根據 name 值獲取,find element_by_id()是根據 id 獲取。 還有根據 XPath、css 選擇器等獲取的方式): from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input_first = browser.find_element_by_id('q')
input_second = browser.find_element_by_xpath('//*[@id ="q"]')
input_third = browser.find_element_by_css_selector('#q')
print(input_first, input_second, input_third)
browser.close()
這里使用 3 種方式獲取輸入框,分別根據 ID, CSS 選擇器和 XPath 獲取,它們返回的結果完全一致。
- 列出所有獲取單個節點的方法:
- 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 Bybrowser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input_first = browser.find_element(By.ID, 'q')
print(input_first)
browser.close()
查找方式的功能與上面列舉函數一致,參數更加靈活。
- 如果有多個節點,用 find_element()方法查找,就只能得到第一個節點。如果要查找所有滿足條件的節點, 需要用 find_elements()方法。注意,這個方法名稱中,element 多了一個s
- 查找淘寶側邊導航條的所有條目: 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()
得到的內容變成了列表類型,列表中的每個節點都是 WebElement 類型。
如果用 find_element()方法,只能獲取匹配的第一個節點,結果是 WebElement類型。如果用 find_elements()方法,結果是列表類型,列表中的每個節點是 WebElement 類型。- 列出所有獲取多個節點的方法:
- find_elements_by_id
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
- ?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()
- Selenium 可以讓瀏覽器模擬執行一些動作。常見的用法有:輸入文字時用 send_keys()方法,清空文字時用 clear()方法,點擊按鈕時用 click()方法。示例: from selenium import webdriver
import time
browser = webdriver.Chrome()
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 ()方法完成搜索動作。
-
更多的操作參見官方文檔交互動作介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。
- 動作鏈?沒有特定的執行對象,如:鼠標拖曳、鍵盤按鍵等。
- 如:現在實現一個節點的拖曳操作,將某個節點從一處拖曳到另外一處,實現代碼: from selenium import webdriver
from selenium.webdriver import ActionChainsbrowser = webdriver.Chrome()
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()方法執行動作。
- 更多動作鏈操作 官方文檔:http://selenium-python.readthedocs.io/api.html#moduleselenium.webdriver.common.action_chains
- 某些操作,Selenium API 并沒有提供。如,下拉進度條,它可以直接模擬運行 JavaScript,此時使用 execute_script()方法即可實現,(注意:execute_script): from selenium import webdriverbrowser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore') browser.execute_script('window.scrollTo(0,document.body.scrollHeight)') browser.execute_script('alert("To Bottom")')
- 通過 page_source 屬性可以獲取網頁的源代碼,接著使用解析庫(如正則表式、Beautiful Soup、pyquery 等)提取信息。?
- 獲取屬性
- 可以使用 get_attribute ()方法來獲取節點的屬性,但是其前提是先選中這個節點,示例: from selenium import webdriver
browser = webdriver.Chrome()
url = 'https://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。
通過 get_attribute()方法,然后傳人想要獲取的屬性名,就可以得到它的值了
- 可以使用 get_attribute ()方法來獲取節點的屬性,但是其前提是先選中這個節點,示例: from selenium import webdriver
browser = webdriver.Chrome()
url = 'https://zhihu.com/explore'
browser.get(url)
logo = browser.find_element_by_id('zh-top-link-logo')
print(logo)
print(logo.get_attribute('class'))
- 獲取文本值
- 每個 WebElement 節點都有 text 屬性,直接調用這個屬性就可以得到節點內部的文本信息,相當于 Beautiful Soup 的 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)
- 獲取id、位置、標簽名和大小
- Web Element 節點還有 些其他屬性,如 id 屬性可以獲取節點 id,location 屬性可以獲取該節點在頁面中的相對位置,tag_name 屬性可以獲取標簽名稱,size 屬性可以獲取節點的大小(寬高),示例: 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)
print(input.id)
print(input.location)
print(input.size)
print(input.tag_name)
首先獲得“提問”按鈕節點,再調用其 id、location、tag_name、size 屬性來獲取對應的屬性值
- Web Element 節點還有 些其他屬性,如 id 屬性可以獲取節點 id,location 屬性可以獲取該節點在頁面中的相對位置,tag_name 屬性可以獲取標簽名稱,size 屬性可以獲取節點的大小(寬高),示例: 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)
print(input.id)
print(input.location)
print(input.size)
print(input.tag_name)
切換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.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 節點(這是不能找到的),如果找不到的話,就會拋出 NoSuchEle entException 異常,異常被捕捉之后,就會輸出 NO LOGO。接下來,重新切換父級 Frame, 然后再次重新獲取節點,發現此時可以成功獲取了。?
當頁面中包含子 Frame 時,如果想獲取子 Frame 中的節點,需要先調用 switch_to.frame() 方法切換到對應的 Frame,再進行操作。
- Selenium 中, get()方法會在網頁框架加載結束后結束執行,此時如果獲取 page_source ,可能并不是瀏覽器完全加載完成的頁面,某些頁面有額外的 Ajax 請求,在網頁驚代碼中也不一定能成功獲取到。所以,這里需要延時等待一定時間,確保節點已經加載出來。
- 延時等待方式有兩種:隱式等待和顯式等待
- 隱式等待
- 使用隱式等待執行測試的時候,如果 Selenium 沒有在 DOM 中找到節點,將繼續等待,超出設定時間后(默認時間為 0 ),則拋出找不到節點的異常。示例: 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)
用 implicitly_wait()方法實現隱式等待。
- 使用隱式等待執行測試的時候,如果 Selenium 沒有在 DOM 中找到節點,將繼續等待,超出設定時間后(默認時間為 0 ),則拋出找不到節點的異常。示例: 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)
- 顯式等待
- 指定要查找的節點,然后指定一個最長等待時間。如果在規定時間內加載出來了這個節點,就返回查找的節點;如果到了規定時間依然沒有加載出該節點, 則拋出超時異常。示例: 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 ECbrowser = webdriver.Chrome()
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_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,‘q’)
- visibility_of_element_located:節點課件,傳入定位元組
- visibility_of:可見,傳入節點對象
- presence_of_all_elements_ilocated:所有節點加載出來
- 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:節點可點擊
- staleness_of:判斷一個節點是否仍在DOM,可判斷頁面是否已經刷新
- element_to_be_selected:節點可選擇,傳節點對象
- element_located_to_be_selected:節點可選擇,傳入定位元組
- element_election_state_to_be:傳入節點對象以及狀態,相等返回True,否則返回False
- element_located_selection_state_to_be:傳入定位元組。以及狀態,相等返回True,否則返回False
- alert_is_present:是否出現警告
- 更多等待條件的參數及用法,參考官方文檔: http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions。
- 對于按鈕,更改一下等待條件,如改為 element_to_be_clickable,也就是可點擊,所以找按鈕時查找 CSS 選擇器為 .btn_search 的按鈕,如果 10 秒內它是可點擊的,也就是成功加載,返回這個按鈕節點;如果超過 10 秒還不可點擊,就拋出異常.
- 指定要查找的節點,然后指定一個最長等待時間。如果在規定時間內加載出來了這個節點,就返回查找的節點;如果到了規定時間依然沒有加載出該節點, 則拋出超時異常。示例: 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 ECbrowser = webdriver.Chrome()
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)
- Selenium 可以完成前進和后退操作,它使用 back()方法后退,使用 forward()方法前進。示例: import time
from selenium import webdriverbrowser = 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()
連續訪問3個頁面,調用 back()方法回到第二個頁面,再調用 forward()方法前進到第 3個頁面。
- Selenium 可以方便地對 Cookies 進行 獲取、添加、刪除等。示例: from selenium import webdriverbrowser = 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。再重新獲取,結果就為空了。
- 訪問網頁時候,會開啟一個個選項卡。Selenium 中,也可以對選項卡進行操作。示例: import time
from selenium import webdriverbrowser = 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.taobap.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()方法,再執行其他操作。
- 在使用 Selenium 過程中,可以使用 try except 語句來捕獲各種異常。(模擬節點未找到異常)示例: from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.find_element_by_id('hello')
首先打開百度頁面,嘗試選擇一個并不存在的節點 ,此時就會遇到異常。捕獲異常示例:
from selenium import webdriver from selenium.common.exceptions import TimeoutException, NoSuchElementExceptionbrowser = webdriver.Chrome() try:browser.get('https://www.baidu.com') except TimeoutException:print('Timeout') try:browser.find_element_by_id('hello') except NoSuchElementException:print('No Element') finally:browser.close()這里使用 try except 來捕獲各類異常。如,對 find_element_by_id()查找節點方法捕獲 NoSuchElementException 異常,一旦出現這樣的錯誤,就進行異常處理,程序也不會中斷。
- 更多異常類,參考官方文檔 http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions。
轉載于:https://www.cnblogs.com/Mack-Yang/p/10184662.html
總結
以上是生活随笔為你收集整理的Class 17 - 1 动态渲染页面爬取 — Selenium使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你的域名是如何变成 IP 地址的?
- 下一篇: Git科普文,Git基本原理各种骚操作