大數(shù)據(jù)時(shí)代,各行各業(yè)對(duì)數(shù)據(jù)采集的需求日益增多,網(wǎng)絡(luò)爬蟲(chóng)的運(yùn)用也更為廣泛,越來(lái)越多的人開(kāi)始學(xué)習(xí)網(wǎng)絡(luò)爬蟲(chóng)這項(xiàng)技術(shù),K哥爬蟲(chóng)此前已經(jīng)推出不少爬蟲(chóng)進(jìn)階、逆向相關(guān)文章,為實(shí)現(xiàn)從易到難全方位覆蓋,特設(shè)【0基礎(chǔ)學(xué)爬蟲(chóng)】專(zhuān)欄,幫助小白快速入門(mén)爬蟲(chóng),本期為自動(dòng)化工具 Selenium 的使用。
概述
目前,很多網(wǎng)站都采用 Ajax 等技術(shù)進(jìn)行動(dòng)態(tài)加載數(shù)據(jù),想要采集這類(lèi)網(wǎng)站的數(shù)據(jù),需要通過(guò)抓包對(duì)網(wǎng)站的數(shù)據(jù)接口進(jìn)行分析,去尋找想要采集的數(shù)據(jù)由哪個(gè)接口傳輸。而且,就算找到了數(shù)據(jù)接口,這些接口可能也是被加密過(guò)的,想要通過(guò)接口獲取數(shù)據(jù),需要對(duì)加密參數(shù)進(jìn)行逆向分析,這個(gè)過(guò)程對(duì)于初學(xué)者來(lái)說(shuō)非常復(fù)雜。
為了解決這些問(wèn)題,能夠更加簡(jiǎn)單的進(jìn)行爬取數(shù)據(jù),我們可以使用到一些自動(dòng)化工具,如 Selenium、playwright、pyppeteer 等,這些工具可以模擬瀏覽器運(yùn)行,直接獲取到數(shù)據(jù)加載完成后的網(wǎng)頁(yè)源碼,這樣我們就可以省去復(fù)雜的抓包、逆向流程,直接拿到數(shù)據(jù)。
Selenium 的使用
介紹
Selenium 是一個(gè)流行的自動(dòng)化測(cè)試框架,可用于測(cè)試 Web 應(yīng)用程序的用戶(hù)界面。它支持多種編程語(yǔ)言,如Java、Python、Ruby等,并提供了一系列 API,可以直接操作瀏覽器進(jìn)行測(cè)試。
安裝
使用 selenium 首先需要下載瀏覽器驅(qū)動(dòng)文件,這里以谷歌瀏覽器為例。在驅(qū)動(dòng)下載頁(yè)面找到與自己瀏覽器版本最為接近的文件,如我的谷歌瀏覽器版本為 112.0.5615.86
,最接近的文件為 112.0.5615.49
,選擇此文件,下載對(duì)應(yīng)系統(tǒng)版本的壓縮包,將壓縮包中的chromedriver.exe程序放到python目錄中。因?yàn)檎G闆r下Python在安裝時(shí)就會(huì)被添加到系統(tǒng)環(huán)境變量之中,將chromedriver.exe放到Python目錄下它就可以在任意位置被執(zhí)行。
添加好驅(qū)動(dòng)文件后需要安裝 Python 的第三方庫(kù) selenium。
pip install selenium
使用
Selenium 支持多種瀏覽器,如谷歌、火狐、Edge、Safari等,這里我們以谷歌瀏覽器為例。
from selenium import webdriver
# 初始化瀏覽器對(duì)象
driver = webdriver.Chrome()
# 驅(qū)動(dòng)瀏覽器打開(kāi)目標(biāo)網(wǎng)址
driver.get('https://www.baidu.com/')
# 打印當(dāng)前頁(yè)面的源代碼
print(driver.page_source)
# 關(guān)閉瀏覽器
driver.quit()
運(yùn)行代碼后我們會(huì)發(fā)現(xiàn)自動(dòng)打開(kāi)了一個(gè)瀏覽器,訪問(wèn)了目標(biāo)網(wǎng)址,在控制臺(tái)輸出了頁(yè)面的源代碼,然后自動(dòng)關(guān)閉。
Selenium 提供了一系列實(shí)用的 Api,通過(guò)它我們可以實(shí)現(xiàn)更多操作。
元素查找
在之前的文章《解析庫(kù)的使用》中,我們已經(jīng)講到了 Xpath、bs4 這兩個(gè)庫(kù)的使用方法,講到了 Xpath 的路徑表達(dá)式和 CSS 選擇器,因此這里主要講解定位方法,路徑表達(dá)式與 CSS 選擇器的使用可以去前文中了解。
以京東首頁(yè)為例,想要獲取秒殺欄目的商品信息,我們可以通過(guò)多種方法來(lái)進(jìn)行定位。
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.jd.com/')
# 根據(jù) Xpath 定位
goods_xpath = driver.find_elements(By.XPATH, '//div[@class="slider_list"]/div/a[@class="slider_item seckill-item slider_active"]')
# 根據(jù) Css 選擇器定位
goods_css = driver.find_elements(By.CSS_SELECTOR, 'a[class="slider_item seckill-item slider_active"]')
# 根據(jù)類(lèi)名定位
goods_class_name = driver.find_elements(By.CLASS_NAME,'seckill-item')
print(goods_xpath)
for goods in goods_xpath:
# 輸出節(jié)點(diǎn)的文本信息
print(goods.text)
driver.quit()
# [<selenium.webdriver.remote.webelement.WebElement(session="f49c1906753e404ca0a017...]
# 歐臻廷保濕修護(hù)亮顏銀霜面霜70ml護(hù)膚品化妝品乳液滋潤(rùn)送女友禮物禮盒款
# ¥1380.00
# Redmi K50Pro 天璣9000 AMOLED 2K柔性直屏 OIS光學(xué)防抖 120W快充 幻鏡 8GB+256GB 5G智能手機(jī) 小米紅米
# ¥2619.00
# 卡詩(shī)(KERASTASE)黑鉆鑰源魚(yú)子醬洗發(fā)水250ml 改善毛躁呵護(hù)受損
# ¥219.00
除了示例代碼中的,還有其它定位方法:
driver.find_elements(By.ID,'ID')
driver.find_elements(By.LINK_TEXT,'LINK_TEXT')
driver.find_elements(By.PARTIAL_LINK_TEXT,'PARTIAL_LINK_TEXT')
driver.find_elements(By.TAG_NAME,'TAG_NAME')
元素交互
Selenium 可以實(shí)現(xiàn)對(duì)頁(yè)面中元素的點(diǎn)擊、輸入等操作。
想要采集京東的指定商品信息,首先需要在輸入框輸入商品名稱(chēng),然后點(diǎn)擊搜索按鈕,網(wǎng)頁(yè)就會(huì)跳轉(zhuǎn)到搜索頁(yè)面,展示我們搜索的商品信息。這個(gè)流程我們也可以通過(guò) Selenium 來(lái)模擬實(shí)現(xiàn)。
driver.get('https://www.jd.com/')
# 獲取搜索框
search = driver.find_element(By.XPATH,'//div[@role="serachbox"]/input')
# 獲取查詢(xún)按鈕
button = driver.find_element(By.XPATH,'//div[@role="serachbox"]/button')
# 在搜索框中輸入 Python
search.send_keys('Python')
# 點(diǎn)擊查詢(xún)按鈕
button.click()
等待
在我們使用 Selenium 時(shí)會(huì)遇到以下兩種情況:
- 頁(yè)面未加載完畢,但是我們需要的元素已經(jīng)加載完畢
- 頁(yè)面加載完畢,但是我們需要的元素為加載完畢
Selenium 的 get 方法是默認(rèn)等待頁(yè)面加載完畢后再執(zhí)行下面的操作。在遇到第一種情況時(shí),要采集的數(shù)據(jù)已經(jīng)生成了,但是可能由于某個(gè)資源加載緩慢導(dǎo)致頁(yè)面一直在加載中狀態(tài),這樣 Selenium 就會(huì)一直等待頁(yè)面完全加載,造成采集速度緩慢等問(wèn)題。而情況二,頁(yè)面已經(jīng)加載完成了,但是要采集的數(shù)據(jù)依舊沒(méi)有渲染出來(lái),這就使 Selenium 定位元素失敗導(dǎo)致程序異常。為了避免解決這兩種情況,我們可以設(shè)置不等待頁(yè)面完全加載,只等待目標(biāo)元素加載完畢。
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
caps = DesiredCapabilities().CHROME
#不等待頁(yè)面加載
caps["pageLoadStrategy"] = "none"
driver = webdriver.Chrome(desired_capabilities=caps)
強(qiáng)制等待
使用 time.sleep() 實(shí)現(xiàn)強(qiáng)制等待。不推薦使用。
driver.get('https://www.jd.com/')
# 強(qiáng)制休眠6秒
time.sleep(6)
隱式等待
等待頁(yè)面加載的時(shí)間,當(dāng)頁(yè)面加載完成后執(zhí)行下一步,如果加載時(shí)間超過(guò)設(shè)置的時(shí)間時(shí)直接執(zhí)行下一步。不推薦使用。
# 隱式等待10秒
driver.implicitly_wait(10)
driver.get('https://www.jd.com/')
顯式等待
等待條件滿(mǎn)足后執(zhí)行下一步,條件不滿(mǎn)足則一直等待,當(dāng)超過(guò)設(shè)置的時(shí)間時(shí)拋出異常。推薦使用。
from selenium import webdriver
import selenium.common.exceptions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get('https://www.jd.com/')
try:
WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, 'a[class="slider_item seckill-item slider_active"]')
)
)
except selenium.common.exceptions.TimeoutException:
print('元素加載超時(shí)')
當(dāng) CSS 選擇器指向的元素存在時(shí)則執(zhí)行下一部,不存在則繼續(xù)等待,直到超過(guò)設(shè)置的10秒,拋出超時(shí)異常。
Actions
上文中講到了元素交互,其中點(diǎn)擊、輸入行為都是屬于 Selenium 的動(dòng)作 Api 之中的,除此之外,Selenium還提供了非常豐富的動(dòng)作 Api,這里只介紹常用的方法。
鼠標(biāo)操作
from selenium.webdriver import ActionChains
# 單擊元素并按住
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver).click_and_hold(clickable).perform()
# 雙擊,將鼠標(biāo)移動(dòng)到元素中心并雙擊
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver).double_click(clickable).perform()
# 按偏移量移動(dòng)鼠標(biāo)
mouse_tracker = driver.find_element(By.ID, "mouse-tracker")
ActionChains(driver).move_to_element_with_offset(mouse_tracker, 8, 0).perform()
# 按當(dāng)前指針位置進(jìn)行偏移,如之前沒(méi)有移動(dòng)鼠標(biāo),則默認(rèn)在窗口的左上角。(13, 15)為橫縱坐標(biāo)的偏移值,13為向右移動(dòng)13,15為向下移動(dòng)15,負(fù)數(shù)則反之。
ActionChains(driver).move_by_offset( 13, 15).perform()
# 按偏移拖放。點(diǎn)擊元素并按鈕,移動(dòng)指定偏移量,然后釋放鼠標(biāo)
draggable = driver.find_element(By.ID, "draggable")
start = draggable.location
finish = driver.find_element(By.ID, "droppable").location
ActionChains(driver).drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y']).perform()
滾輪
# 滾動(dòng)到指定元素
iframe = driver.find_element(By.TAG_NAME, "iframe")
ActionChains(driver).scroll_to_element(iframe).perform()
# 按給定值滾動(dòng),(0, delta_y) 為向右和向下滾動(dòng)的量,負(fù)值則反之。
footer = driver.find_element(By.TAG_NAME, "footer")
delta_y = footer.rect['y']
ActionChains(driver).scroll_by_amount(0, delta_y).perform()
反檢測(cè)
Selenium 有著非常明顯的缺陷,就是容易被網(wǎng)站檢測(cè)到。我們通過(guò) Selenium 打開(kāi)網(wǎng)頁(yè)時(shí)會(huì)發(fā)現(xiàn),窗口上方會(huì)顯示瀏覽器正受到自動(dòng)測(cè)試軟件的控制,這就說(shuō)明 Selenium 驅(qū)動(dòng)瀏覽器與用戶(hù)正常打開(kāi)瀏覽器是不同的,它存在著許多 WebDriver 的特征,網(wǎng)站可以通過(guò)檢測(cè)這些特征來(lái)禁止 Selenium 訪問(wèn)。
我們可以通過(guò)一些特征值檢測(cè)的網(wǎng)站來(lái)對(duì)比正常訪問(wèn)與 Selenium 訪問(wèn)的區(qū)別。
上面是正常訪問(wèn),下面是 Selenium 訪問(wèn),可以很清晰的看到 WebDriver 一欄標(biāo)紅了,這就說(shuō)明 Selenium 被檢測(cè)到了。網(wǎng)站的檢測(cè)原理主要是通過(guò)檢查 window.navigator 對(duì)象中是否存在 webdriver 屬性。我們了解到這一點(diǎn)后,可以通過(guò)一些操作來(lái)修改window.navigator 對(duì)象,在頁(yè)面未加載時(shí)將它的 webdriver 屬性設(shè)置為 false,這樣或許就能避開(kāi)網(wǎng)站的檢測(cè)機(jī)制。
from selenium import webdriver
from selenium.webdriver import ChromeOptions
options = ChromeOptions()
# 以最高權(quán)限運(yùn)行
options.add_argument('--no-sandbox')
# navigator.webdriver 設(shè)置為 false
options.add_argument("--disable-blink-features=AutomationControlled")
# 隱藏"Chrome正在受到自動(dòng)軟件的控制"提示
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options)
with open('./stealth.min.js', 'r') as f:
js = f.read()
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {'source': js})
可以看到,我們進(jìn)行了一些隱藏特征的操作,但在最后我們讀取一個(gè)文件,然后將這個(gè)文件信息傳入到了execute_cdp_cmd()方法中,這個(gè)操作其實(shí)也是在隱藏特征。
stealth.min.js 來(lái)自于 puppeteer 的一個(gè)插件,puppeteer 是一個(gè)控制 headless Chrome 的 Node.js API ,puppeteer 有一個(gè)插件名為 puppeteer-extra-plugin-stealth,它的開(kāi)發(fā)目的就是為了防止 puppeteer 被檢測(cè),它可以隱藏許多自動(dòng)化特征。puppeteer-extra 的作者也編寫(xiě)了一個(gè)腳本,用于將最新的特征隱藏方法puppeteer-extra-stealth 提取到 JS 文件之中,生成的 JS 文件可以用于純 CDP 實(shí)現(xiàn),也可以用于測(cè)試開(kāi)發(fā)工具中的檢測(cè)規(guī)避。而 Selenium 正好支持 CDP 的調(diào)用,CDP 全稱(chēng)(Chrome DevTools Protocol),利用它可以在瀏覽器加載之前執(zhí)行 JS 語(yǔ)句。
如果你已經(jīng)安裝了 node.js ,npx extract-stealth-evasions
執(zhí)行此命令就可以生成 stealth.min.js 文件。下圖就隱藏特征后訪問(wèn)結(jié)果。
無(wú)頭模式
無(wú)頭模式下網(wǎng)站運(yùn)行不會(huì)彈出窗口,可以減少一些資源消耗,也避免了瀏覽器窗口運(yùn)行時(shí)對(duì)設(shè)備正常使用帶來(lái)的影響,在服務(wù)器上運(yùn)行需要用到。但是無(wú)頭模式下被網(wǎng)站檢測(cè)的特征點(diǎn)非常多,因此需要根據(jù)自己的應(yīng)用場(chǎng)景來(lái)使用。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-419505.html
options = ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
總結(jié)
使用 Selenium 來(lái)進(jìn)行數(shù)據(jù)的爬取是一種優(yōu)勢(shì)與劣勢(shì)都非常明顯的選擇。它的優(yōu)勢(shì)就是簡(jiǎn)單,不需要對(duì)網(wǎng)站進(jìn)行調(diào)試,不需要關(guān)注數(shù)據(jù)的來(lái)源,大大減少了爬蟲(chóng)程序的開(kāi)發(fā)時(shí)間。它的劣勢(shì)有多種:采集效率低,資源占用大,不穩(wěn)定,容易被檢測(cè),且需要依賴(lài)于 WebDriver,當(dāng)瀏覽器更新后就需要更新對(duì)應(yīng)的 WebDriver。因此 Selenium 適用于那些逆向難度較大,且對(duì)采集效率要求不高的場(chǎng)景。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-419505.html
到了這里,關(guān)于【0基礎(chǔ)學(xué)爬蟲(chóng)】爬蟲(chóng)基礎(chǔ)之自動(dòng)化工具 Selenium 的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!