JavaScript動態(tài)渲染界面爬取-Selenium的簡單學習
引言:在學習這一章之前,若之前對于Ajax數(shù)據(jù)的分析和爬取有過了解的會知道,Ajax是JavaScript動態(tài)渲染界面的一種情形,通過直接分析Ajax,使我們?nèi)匀豢梢越柚鷕equests或urllib實現(xiàn)數(shù)據(jù)爬取。不過JavaScript動態(tài)渲染的界面不止Ajax一種,而且在實際中Ajax接口中會含有很多加密參數(shù),比如說xhr中request url的鏈接中含有token參數(shù)使我們難以找到規(guī)律,所以很難直接通過分析Ajax爬取數(shù)據(jù)。
? Python提供了許多模擬瀏覽器運行的庫,例如Selenium、Splash、Pyppetter、Playwright等,可以幫我們實現(xiàn)所見皆所爬,就不用再為如何爬取動態(tài)渲染的界面發(fā)愁了。
1. Selenium準備工作
Selenium是一個自動化測試工具,利用它可以驅(qū)動瀏覽器完成特定的操作,例如點擊、下拉等,你還可以利用這個工具來惡搞別人瀏覽不良網(wǎng)站!??!言歸正傳,你可以利用它獲取瀏覽器當前呈現(xiàn)的頁面源代碼,做到所見皆所爬,對于一些JavaScript動態(tài)渲染的界面來說,這種爬取方式非常有效。 本節(jié)以Chrome瀏覽器為例來講解Selenium。首先先安裝好Chrome和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
import time
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)
time.sleep(1000)
finally:
browser.close()
運行代碼后,會自動彈出一個Chrome瀏覽器,接著瀏覽器會跳轉到百度界面并在搜索框中輸入Python,就會自動跳轉到搜索結果頁。輸出頁面源代碼,此處省略源代碼,可以知道,我們得到的當前URL、Cookie和頁面源代碼都是真實內(nèi)容,而用requests請求的源代碼卻和使用Selenium的源代碼不一樣。所以說,用Selenium驅(qū)動瀏覽器加載的頁面可以直接拿到JavaScript渲染的結果,無須關心是什么加密系統(tǒng)。
2. Selenium簡單用法
2.1. 初始化瀏覽器對象-webdriver.Chrome()
Selenium支持的瀏覽器非常多,既有Chrome、Firefox、Edge、Safari等電腦端的瀏覽器,也有Android、BlackBerry等手機端的瀏覽器。下面是初始化瀏覽器對象:
from selenium import webdriver
browser1 = webdriver.Chrome()
# browser2 = webdriver.Firefox()
# browser3 = webdriver.Edge()
print(type(browser1)) # <class 'selenium.webdriver.chrome.webdriver.WebDriver'>
browser1.close()
2.2. 訪問界面-browser.get()
上面我們已經(jīng)初始化了瀏覽器對象,接下來就是要調(diào)用browser,執(zhí)行各個方法模擬瀏覽器操作。訪問界面:
from selenium import webdriver
url = "https://www.taobao.com/"
browser1 = webdriver.Chrome()
browser1.get(url)
browser1.close()
2.3. 查找節(jié)點-find_element()
我們想從淘寶頁面提取搜索框這個節(jié)點,那就進入開發(fā)者選項快捷搜索就行了。
from selenium import webdriver
from selenium.webdriver.common.by import By
url = "https://www.taobao.com/"
browser1 = webdriver.Chrome()
browser1.get(url)
input_first = browser1.find_element(By.ID, "q")
print(input_first)
# <selenium.webdriver.remote.webelement.WebElement (session="dc29f31092c770c57925bbd74839af9d", element="C15004B057068D353AA2B162C5D9F24E_element_2")>
browser1.close()
但是這種方法只會查找第一個匹配的節(jié)點,要是想要查找多個節(jié)點那就使用find_elements(),這時返回的內(nèi)容是列表類型的,列表里面每一個元素都是WebElement類型的。
2.4. 節(jié)點交互-send_keys()、clear()、click()
Selenium可以驅(qū)動瀏覽器執(zhí)行一些操作。比較常見的用法有:用send_keys方法輸入文字,用clear方法清空文字,用click方法點擊按鈕:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
url = "https://www.taobao.com/"
browser1 = webdriver.Chrome()
browser1.get(url)
# 查找對話框,按鈕
input_first = browser1.find_element(By.ID, "q")
button = browser1.find_element(By.CLASS_NAME, "btn-search")
# 輸入文字
input_first.send_keys("IPad")
time.sleep(3)
# 清空文字
input_first.clear()
# 輸入文字
input_first.send_keys("HUAWEI")
# 點擊按鈕
button.click()
time.sleep(3)
browser1.close()
更多操作請見官方文檔
https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement
2.5. 動作鏈-ActionChains()
在上面示例中,交互操作都是針對某一個節(jié)點來執(zhí)行的。其實還有一些操作,他們沒有特定的執(zhí)行對象,比如鼠標拖拽、按鍵鍵盤等,這些操作需要另一種方式執(zhí)行,那就是動作鏈。例如可以這樣實現(xiàn)拖拽節(jié)點的操作:
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
# 切換到名為 'iframeResult' 的 iframe 內(nèi)容(這個網(wǎng)頁被分為很多個框架)
browser.switch_to.frame('iframeResult')
# 找到要拖拽的源元素和目標元素
source = browser.find_element(By.CSS_SELECTOR, '#draggable')
target = browser.find_element(By.CSS_SELECTOR,'#droppable')
# 創(chuàng)建 ActionChains 對象來執(zhí)行操作
actions = ActionChains(browser)
# 進行拖拽操作
actions.drag_and_drop(source, target)
# 執(zhí)行
actions.perform()
time.sleep(5)
更多的動作鏈請參考官方文檔的介紹:
https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains
2.6. 運行JavaScript-execute_script()
還有一些操作,Selenium沒有提供API,例如下拉進度條,面對這種情況可以模擬運行JavaScript,此時使用execute_script方法可以實現(xiàn),代碼如下:
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")')
2.7. 獲取節(jié)點信息-get_attribute()、text、id、location、tag_name、size
當我們選中一個節(jié)點時候,我們可以獲取它的屬性、文本、ID、位置、標簽名和大?。?/p>
- get_attribute():獲取屬性,參數(shù)傳入屬性名,額外:textContent子孫文本,innerHTML當前html代碼,class獲取元素的CSS類,id,src,class等
- text:獲取文本
- id:獲取id
- location:獲取位置
- tag_name:獲取標簽名
- size:獲取大小
2.8. 切換Frame-switch_to.frame()
網(wǎng)頁中有一種節(jié)點叫做iframe,也就是子Frame,相當于頁面中的子頁面,它的結構和外部網(wǎng)頁的結構完全一致。Selenium打開一個頁面之后,默認是在父Frame里操作,此時如果頁面中含有子Frame,是不能在子Frame中操作的,這時就需要使用switch_to.frame方法切換Frame。
需要注意的是,當你在子frame刷新出新的東西的時候,需要將frame切換回頂級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)
2.9. 延時等待
在Selenium中,get方法在網(wǎng)頁框架結束后才會結束執(zhí)行,如果我們嘗試在get方法執(zhí)行完畢時獲取網(wǎng)頁源代碼,可能結果不是瀏覽器加載完全的頁面,因為某些頁面需要額外的Ajax請求還會經(jīng)過JavaScript渲染,所以有必要讓瀏覽器延時一段時間。這里的等待方式有兩種,一種是隱式等待,一種是顯式等待。
2.9.1. 隱式等待-implicitly_wait()
使用隱式等待執(zhí)行測試時,在查找節(jié)點而節(jié)點沒有立即出現(xiàn)時,隱式等待會先等待一段時間再查找DOM,默認的等待時間為0
from selenium import webdriver
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
# 設置隱式等待時間
browser.implicitly_wait(10)
browser.get("https://spa2.scrape.center/")
input_first = browser.find_element(By.CLASS_NAME, "logo-image")
print(input_first.get_attribute("src"))
2.9.2. 顯式等待-WebDriverWait()
隱式等待的效果其實并不好,因為我們只規(guī)定了一個固定時間,而頁面加載時間會受網(wǎng)絡條件的影響。還有一種更合適的等待方法——顯式等待,這種方式會指定查找的節(jié)點和最長等待時間,如果在規(guī)定時間內(nèi)加載出了要查找的節(jié)點,就返回這個節(jié)點;如果到規(guī)定時間內(nèi)依然沒有加載出節(jié)點,就拋出超時異常。
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.Chrome()
browser.get("https://taobao.com")
# 設定wait對象,指定最長等待時間
wait = WebDriverWait(browser, 10)
# 調(diào)用wait的until方法,傳入等待條件
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)
下面表格列出了所有等待條件(應該)
等待條件(Expected Conditions) | 描述 |
---|---|
EC.presence_of_element_located |
元素出現(xiàn)在 DOM 中。 |
EC.visibility_of_element_located |
元素在頁面上可見。 |
EC.visibility_of |
元素在頁面上可見。 |
EC.invisibility_of_element_located |
元素在頁面上不可見。 |
EC.element_to_be_clickable |
元素可被點擊。 |
EC.element_to_be_selected |
元素被選中。 |
EC.element_located_to_be_selected |
元素被選中。 |
EC.text_to_be_present_in_element |
在元素中出現(xiàn)特定文本。 |
EC.text_to_be_present_in_element_value |
在元素的值中出現(xiàn)特定文本。 |
EC.frame_to_be_available_and_switch_to_it |
切換到指定的 iframe 或 frame 中。 |
EC.element_to_be_located |
元素出現(xiàn)在 DOM 中。 |
EC.staleness_of |
元素不再附加到 DOM 上(已過時)。 |
EC.element_located_selection_state_to_be |
元素的選中狀態(tài)與給定狀態(tài)匹配。 |
EC.element_selection_state_to_be |
元素的選中狀態(tài)與給定狀態(tài)匹配。 |
EC.alert_is_present |
存在警報。 |
EC.title_contains |
頁面標題包含指定文本。 |
EC.title_is |
頁面標題等于指定文本。 |
EC.url_contains |
頁面 URL 包含指定文本。 |
EC.url_to_be |
頁面 URL 完全等于指定 URL。 |
EC.url_matches |
頁面 URL 匹配指定的正則表達式。 |
EC.invisibility_of_element_located |
元素在頁面上不可見。 |
更多等待條件和參數(shù)及其用法可以參考官方文檔:
https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions
2.10. 前進和后退-forward()、back()
平時使用瀏覽器時也有前進和后退的功能,用Selenium也可以完成這個操作。
from selenium import webdriver
import time
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(3)
browser.forward()
time.sleep(3)
browser.close()
2.11. Cookie-add_cookie()、delete_all_cookies()
使用selenium還可以對Cookie進行操作
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.zhihu.com/explore")
print(browser.get_cookies())
cookie = {'name': 'AJ', 'value': 'BJ'}
browser.add_cookie(cookie)
print(browser.get_cookies())
browser.delete_all_cookies() # 去掉all也可以指定刪除哪一個
print(browser.get_cookies())
2.12. 選項卡管理-execute_script()
訪問網(wǎng)頁的時候,會開啟一個個選項卡。在Selenium中,我們也可以對選項卡進行操作:
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(2)
# 切換回去
browser.switch_to.window(browser.window_handles[0])
browser.get("https://python.org")
2.13. 切換列表-Select()
在訪問網(wǎng)頁的時候可能會發(fā)現(xiàn)一個列表,而它的網(wǎng)頁源代碼類似是這樣的:
<select name="week" id="week" onchange="se()">
<option value="">--請選擇--</option>
<option value="2023-09-03" selected="">第一周</option>
<option value="2023-09-10">第二周</option>
<option value="2023-09-17">第三周</option>
</select>
這是一個html中的元素,通常用于創(chuàng)建下拉列表(也稱為下拉框或者下拉菜單),而上面的οnchange="se()"是一個JavaScript事件處理程序,可以讓用戶在不同周時執(zhí)行特定的JavaScript。下面是交互的代碼:
from selenium import webdriver
from selenium.webdriver.support.ui import Select
driver = webdriver.Chrome('chromedriver.exe') # 請?zhí)鎿Q為您的WebDriver路徑
driver.get('URL_你的網(wǎng)頁地址')
# 選擇下拉列表元素 --- 1
week_dropdown = driver.find_element_by_id('week')
# 創(chuàng)建一個Select對象,以便與下拉列表交互 --- 2
week_select = Select(week_dropdown)
# 通過文本值選擇第二周 --- 3
week_select.select_by_visible_text('第二周')
# 通過值選擇第三周 --- 3
week_select.select_by_value('2023-09-17')
# 獲取當前選中的選項的文本和值
selected_option = week_select.first_selected_option
print('當前選中的文本:', selected_option.text)
print('當前選中的值:', selected_option.get_attribute('value'))
# 關閉瀏覽器
driver.quit()
3. 反屏蔽
現(xiàn)在有很多網(wǎng)站增加了對Selenium的檢測,防止一些爬蟲的惡意爬取,如果檢測到有人使用Selenium打開瀏覽器就會直接屏蔽。大多數(shù)情況下,檢測的基本原理時檢測當前瀏覽器窗口下的window.navigator對象中是否包含webdriver屬性。是因為正常使用瀏覽器時,這個屬性應該是undefined,一旦使用了Selenium。它就會給window.navigator對象設置為webdriver屬性。很多網(wǎng)站會通過JavaScript語句判斷是否存在webdriver屬性。
在Selenium中,可以用CDP(即Chrome Devtools Protocol,Chrome 開發(fā)工具協(xié)議)解決這個問題,利用它可以實現(xiàn)在每個頁面剛加載的時候就執(zhí)行JavaScript語句,將webdriver屬性置空。這里執(zhí)行的CDP方法叫做Page.addScriptToEvaluateOnNewDocument,將上面的JavaScript語句傳入其中即可。另外,還可以加入幾個選項來隱藏WebDriver提示條和自動化擴展信息。
from selenium import webdriver
from selenium.webdriver import ChromeOptions
import time
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(options=option)
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
})
browser.get('https://antispider1.scrape.center/')
time.sleep(10)
4. 無頭模式
上面的案例在運行的時候總會彈出一個瀏覽器窗口,我們也可以把它去掉,這樣可以減少一些資源的加載:文章來源:http://www.zghlxwxcb.cn/news/detail-696428.html
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument("--headless")
browser = webdriver.Chrome(options= option)
browser.set_window_size(1366, 768)
browser.get("https://www.baidu.com")
browser.get_screenshot_as_file("preview.png")
?文章來源地址http://www.zghlxwxcb.cn/news/detail-696428.html
到了這里,關于【爬蟲】7.1. JavaScript動態(tài)渲染界面爬取-Selenium的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!