數(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)】專欄,幫助小白快速入門爬蟲(chóng),本期為自動(dòng)化工具 playwright 的使用。
上期文章中講到了自動(dòng)化工具 Selenium 的基本使用方法,也介紹了 Selenium 的優(yōu)缺點(diǎn)。Selenium的功能非常強(qiáng)大,支持所有現(xiàn)代瀏覽器。但是 Selenium 使用起來(lái)十分不方便,我們需要提前安裝好瀏覽器,然后下載對(duì)應(yīng)版本的驅(qū)動(dòng)文件,當(dāng)瀏覽器更新后驅(qū)動(dòng)文件也得隨之更新。如果想要大規(guī)模且長(zhǎng)期的采集數(shù)據(jù),那么部署 Selenium 時(shí)環(huán)境配置會(huì)是一個(gè)大問(wèn)題。因此本期我們將介紹一款更加好用的自動(dòng)化工具 Playwright 。
Playwright是一個(gè)用于自動(dòng)化Web瀏覽器測(cè)試和Web數(shù)據(jù)抓取的開(kāi)源庫(kù)。它由Microsoft開(kāi)發(fā),支持Chrome、Firefox、Safari、Edge和WebKit瀏覽器。Playwright的一個(gè)主要特點(diǎn)是它能夠在所有主要的操作系統(tǒng)(包括Windows、Linux和macOS)上運(yùn)行,并且它提供了一些強(qiáng)大的功能,如跨瀏覽器測(cè)試、支持無(wú)頭瀏覽器、并行執(zhí)行測(cè)試、元素截圖和模擬輸入等。它主要有以下優(yōu)勢(shì):
使用 Playwright 需要 Python版本在3.7以上。
安裝 Playwright 可以直接使用 pip 工具:
pip install playwright
安裝完成后需要進(jìn)行初始化操作,安裝所需的瀏覽器。
playwright install
執(zhí)行上述指令時(shí),Playwright 會(huì)自動(dòng)安裝多個(gè)瀏覽器(Chromium、Firefox 和 WebKit)并配置驅(qū)動(dòng),所以速度較慢。
Playwright 支持同步與異步兩種模式,這里分開(kāi)來(lái)進(jìn)行講解。
使用 Playwright 時(shí)可以選擇啟動(dòng)安裝的三種瀏覽器(Chromium、Firefox 和 WebKit)中的一種。
from playwright.sync_api import sync_playwright
# 調(diào)用sync_playwright方法,返回瀏覽器上下文管理器
with sync_playwright() as p:
# 創(chuàng)建谷歌瀏覽器示例,playwright默認(rèn)啟動(dòng)無(wú)頭模式,設(shè)置headless=False,即關(guān)閉無(wú)頭模式
browser=p.chromium.launch(headless=False)
# 新建選項(xiàng)卡
page=browser.new_page()
# 跳轉(zhuǎn)到目標(biāo)網(wǎng)址
page.goto("http://baidu.com")
# 獲取頁(yè)面截圖
page.screenshot(path='example.png')
# 打印頁(yè)面的標(biāo)題,也就是title節(jié)點(diǎn)中的文本信息
print(page.title())
# 關(guān)閉瀏覽器
browser.close()
# 輸出:百度一下,你就知道
可以看到,Playwright 的使用也比較簡(jiǎn)單,語(yǔ)法比較簡(jiǎn)潔,而且瀏覽器的啟動(dòng)速度以及運(yùn)行速度也很快。
異步代碼的編寫(xiě)方法與同步基本一致,區(qū)別在于同步調(diào)用的是 sync_playwright,異步調(diào)用的是 async_playwright。最終運(yùn)行效果與同步一致。
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser=await p.chromium.launch(headless=False)
page=await browser.new_page()
await page.goto("http://baidu.com")
# 打印網(wǎng)頁(yè)源代碼
print(await page.content())
await browser.close()
asyncio.run(main())
Playwright 提供了代碼生成功能,這個(gè)功能可以對(duì)我們?cè)跒g覽器上的操作進(jìn)行錄制并生成代碼,它可以有效提高程序的編寫(xiě)效率。代碼生成功能需要使用 Playwright 命令行中的 codegen實(shí)現(xiàn),codegen 命令存在如下主要參數(shù):
-o :將生成的腳本保存到指定文件
--target :生成的語(yǔ)言,默認(rèn)為 Python
--save-trace :記錄會(huì)話的跟蹤并將其保存到文件中
-b :要使用的瀏覽器,默認(rèn)為 chromium
--timeout :設(shè)置頁(yè)面加載的超時(shí)時(shí)間
--user-agent :指定UA
--viewport-size :指定瀏覽器窗口大小
我們?cè)诿钚袌?zhí)行命令:playwright codegen -o script.py
執(zhí)行命令后會(huì)彈出一個(gè) chromium 瀏覽器與腳本窗口,當(dāng)我們?cè)跒g覽器上進(jìn)行操作時(shí),腳本窗口會(huì)根據(jù)我們的操作生成對(duì)應(yīng)代碼。當(dāng)我們操作結(jié)束后,關(guān)閉瀏覽器,在當(dāng)前目錄下會(huì)生成一個(gè) script.py 文件,該文件中就是我們?cè)谶M(jìn)行瀏覽器操作時(shí),Playwright 錄制的代碼。我們運(yùn)行該文件,就會(huì)發(fā)現(xiàn)它在復(fù)現(xiàn)我們之前的操作。
代碼生成功能的實(shí)用性其實(shí)較為一般,它只能實(shí)現(xiàn)比較簡(jiǎn)單的操作,當(dāng)遇到復(fù)雜操作時(shí),生成的代碼就容易出現(xiàn)問(wèn)題。最好的方式是使用代碼生成功能生成部分操作的代碼,然后再手動(dòng)去修改它生成的代碼。
上一步中,我們使用代碼生成功能生成了一段代碼,我們會(huì)發(fā)現(xiàn)這段代碼中使用到了一個(gè) new_context 方法,通過(guò)這個(gè)方法創(chuàng)建了一個(gè) content ,然后再去進(jìn)行其它操作。這個(gè) new_content 方法其實(shí)是為了創(chuàng)建一個(gè)獨(dú)立的全新上下文環(huán)境,它的目的是為了防止多個(gè)測(cè)試用例并行時(shí)各個(gè)用例間不受干擾,當(dāng)一個(gè)測(cè)試用例異常時(shí)不會(huì)影響到另一個(gè)。
browser=playwright.chromium.launch()
context=browser.new_context()
page=context.new_page()
Playwright 提供了多種定位器來(lái)幫助開(kāi)發(fā)中定位元素。
page.get_by_role() :通過(guò)顯式和隱式可訪問(wèn)性屬性進(jìn)行定位。
page.get_by_text() :通過(guò)文本內(nèi)容定位。
page.get_by_label() :通過(guò)關(guān)聯(lián)標(biāo)簽的文本定位表單控件。
page.get_by_placeholder() :按占位符定位輸入。
page.get_by_alt_text() :通過(guò)替代文本定位元素,通常是圖像。
page.get_by_title() :通過(guò)標(biāo)題屬性定位元素。
page.get_by_test_id() :根據(jù)data-testid屬性定位元素(可以配置其他屬性)。
page.locator():拓展選擇器,可以使用 CSS 選擇器進(jìn)行定位
使用定位器最好的方式就是上文中講到的利用代碼生成功能來(lái)生成定位代碼,然后手動(dòng)去修改,這里就不做嘗試。
Playwright 支持 CSS、Xpath 和一些拓展選擇器,提供了一些比較方便的使用規(guī)則。
CSS 選擇器
# 匹配 button 標(biāo)簽
page.locator('button').click()
# 根據(jù) id 匹配,匹配 id 為 container 的節(jié)點(diǎn)
page.locator('#container').click()
# CSS偽類匹配,匹配可見(jiàn)的 button 按鈕
page.locator("button:visible").click()
# :has-text 匹配任意內(nèi)部包含指定文本的節(jié)點(diǎn)
page.locator(':has-text("Playwright")').click()
# 匹配 article 標(biāo)簽內(nèi)包含 products 文本的節(jié)點(diǎn)
page.locator('article:has-text("products")').click()
# 匹配 article 標(biāo)簽下包含類名為 promo 的 div 標(biāo)簽的節(jié)點(diǎn)
page.locator("article:has(div.promo)").click()
Xpath
page.locator("xpath=//button").click()
page.locator('xpath=//div[@class="container"]').click()
其它
# 根據(jù)文本匹配,匹配文本內(nèi)容包含 name 的節(jié)點(diǎn)
page.locator('text=name').click()
# 匹配文本內(nèi)容為 name 的節(jié)點(diǎn)
page.locator("text='name'").click()
# 正則匹配
page.locator("text=/name\s\w+word").click()
# 匹配第一個(gè) button 按鈕
page.locator("button").locator("nth=0").click()
# 匹配第二個(gè) button 按鈕
page.locator("button").locator("nth=-1").click()
# 匹配 id 為 name 的元素
page.locator('id=name')
當(dāng)進(jìn)行 click 、fill 等操作時(shí),Playwright 在采取行動(dòng)之前會(huì)對(duì)元素執(zhí)行一系列可操作性檢測(cè),以確保這些行動(dòng)能夠按預(yù)期進(jìn)行。
如對(duì)元素進(jìn)行 click 操作之前,Playwright 將確保:
元素附加到 DOM
元素可見(jiàn)
元素是穩(wěn)定的,因?yàn)闆](méi)有動(dòng)畫(huà)或完成動(dòng)畫(huà)
元素接收事件,因?yàn)闆](méi)有被其他元素遮擋
元素已啟用
即使 Playwright 已經(jīng)做了充分準(zhǔn)備,但是也并不完全穩(wěn)定,在實(shí)際項(xiàng)目中依舊容易出現(xiàn)因頁(yè)面加載導(dǎo)致事件沒(méi)有生效等問(wèn)題,為了避免這些問(wèn)題,需要自行設(shè)置等待。
# 固定等待1秒
page.wait_for_timeout(1000)
# 等待事件
page.wait_for_event(event)
# 等待加載狀態(tài)
page.get_by_role("button").click()
page.wait_for_load_state()
添加/刪除事件
from playwright.sync_api import sync_playwright
def print_request_sent(request):
print("Request sent: " + request.url)
def print_request_finished(request):
print("Request finished: " + request.url)
with sync_playwright() as p:
browser=p.chromium.launch(headless=False)
page=browser.new_page()
# 添加事件 發(fā)起請(qǐng)求時(shí)打印URL
page.on("request", print_request_sent)
# 請(qǐng)求完成時(shí)打印URL
page.on("requestfinished", print_request_finished)
page.goto("https://baidu.com")
# 刪除事件
page.remove_listener("requestfinished", print_request_finished)
browser.close()
在 Selenium 的使用中,我們講到了自動(dòng)化工具容易被網(wǎng)站檢測(cè),也提供了一些繞過(guò)檢測(cè)的方案。這里我們介紹一下 Playwright 的反檢測(cè)方案。
以 https://bot.sannysoft.com/ 為例,我們分別測(cè)試正常模式與無(wú)頭模式下的檢測(cè)結(jié)果。
正常模式:
無(wú)頭模式:
可以看到,正常模式下 WebDriver 一欄報(bào)紅,而無(wú)頭模式下更是慘不忍睹,基本上所有特征都被檢測(cè)到了。這些還只是最基本的檢測(cè)機(jī)制,自動(dòng)化工具的弱點(diǎn)就暴露的很明顯了。
與 Selenium 一樣,繞過(guò)檢測(cè)主要還是針對(duì)網(wǎng)站的檢測(cè)機(jī)制來(lái)處理,主要就是在頁(yè)面加載之前通過(guò)執(zhí)行 JS 代碼來(lái)修改一些瀏覽器特征。以無(wú)頭模式為例:
from playwright.sync_api import sync_playwright
with open('./stealth.min.js', 'r') as f:
js=f.read()
with sync_playwright() as p:
browser=p.chromium.launch()
# 添加 UserAgent
page=browser.new_page(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
)
# 執(zhí)行 JS 代碼
page.add_init_script(js)
page.goto("https://bot.sannysoft.com/")
page.screenshot(path='example.png')
browser.close()
這里與 Selenium 反檢測(cè)方案一樣,執(zhí)行 stealth.min.js 來(lái)隱藏特征( stealth.min.js 的來(lái)源與介紹參考上期文章)。最終結(jié)果如下圖:
可以看到,與真實(shí)瀏覽器訪問(wèn)基本一致了。
與 Selenium 相比,Playwright 最大的優(yōu)點(diǎn)就是不需要手動(dòng)安裝驅(qū)動(dòng),而且它擁有更好的性能與更多的功能。因此 在爬蟲(chóng)領(lǐng)域,Playwright 是更好的選擇。
文將介紹使用C#編程語(yǔ)言實(shí)現(xiàn)自動(dòng)登錄網(wǎng)頁(yè)、瀏覽頁(yè)面并抓取數(shù)據(jù)的方法。
1.登錄功能:
通過(guò)C#編寫(xiě)代碼,實(shí)現(xiàn)自動(dòng)模擬用戶登錄網(wǎng)頁(yè)的功能。可以使用WebClient類或HttpWebRequest類來(lái)發(fā)送POST請(qǐng)求,將用戶名和密碼作為參數(shù)傳遞給服務(wù)器,以實(shí)現(xiàn)自動(dòng)登錄。
2.頁(yè)面瀏覽:
利用C#的WebBrowser控件,可以模擬用戶對(duì)網(wǎng)頁(yè)進(jìn)行瀏覽操作。可以設(shè)置WebBrowser控件的Url屬性來(lái)加載指定的網(wǎng)頁(yè),并使用Navigate方法實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)。
3.頁(yè)面元素操作:
在頁(yè)面加載完成后,可以通過(guò)C#代碼對(duì)頁(yè)面元素進(jìn)行操作。例如,可以使用HtmlAgilityPack類庫(kù)來(lái)解析HTML文檔,根據(jù)元素的XPath或CSS選擇器定位到指定的元素,并讀取或修改其屬性值。
4.數(shù)據(jù)抓?。?/b>
通過(guò)分析頁(yè)面結(jié)構(gòu)和使用合適的選擇器,可以用C#代碼抓取頁(yè)面中所需的數(shù)據(jù)。可以使用正則表達(dá)式、XPath或CSS選擇器等方式來(lái)定位和提取目標(biāo)數(shù)據(jù),并將其保存到變量或數(shù)據(jù)庫(kù)中供后續(xù)處理。
5.數(shù)據(jù)處理與分析:
獲取到抓取的數(shù)據(jù)后,可以進(jìn)行進(jìn)一步的處理和分析??梢允褂肅#的數(shù)據(jù)處理類庫(kù),如LINQ或DataTable,對(duì)數(shù)據(jù)進(jìn)行篩選、排序、統(tǒng)計(jì)等操作,以得到所需的結(jié)果。
6.定時(shí)任務(wù):
利用C#的定時(shí)任務(wù)功能,可以實(shí)現(xiàn)定時(shí)自動(dòng)執(zhí)行抓取數(shù)據(jù)的操作??梢允褂肨imer類或Quartz.NET等工具來(lái)設(shè)置定時(shí)觸發(fā)器,定期調(diào)用抓取數(shù)據(jù)的代碼。
7.異常處理:
在編寫(xiě)自動(dòng)登錄網(wǎng)頁(yè)并抓取數(shù)據(jù)的程序時(shí),需要考慮各種異常情況的處理。例如,網(wǎng)絡(luò)連接異常、頁(yè)面加載超時(shí)、元素定位失敗等情況都需要進(jìn)行相應(yīng)的錯(cuò)誤處理,并給出友好的提示信息。
8.數(shù)據(jù)存儲(chǔ)與展示:
抓取到的數(shù)據(jù)可以保存到本地文件或數(shù)據(jù)庫(kù)中,并通過(guò)C#編程實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ)和展示??梢允褂肁DO.NET來(lái)連接數(shù)據(jù)庫(kù)并執(zhí)行相關(guān)操作,也可以使用第三方庫(kù)如Dapper或Entity Framework簡(jiǎn)化數(shù)據(jù)庫(kù)操作。
以上就是使用C#實(shí)現(xiàn)自動(dòng)登錄網(wǎng)頁(yè)、瀏覽頁(yè)面并抓取數(shù)據(jù)的方法。通過(guò)這些技術(shù)手段,我們可以更高效地獲取所需的數(shù)據(jù),并進(jìn)行進(jìn)一步的處理和分析,為我們提供更多有價(jià)值的信息。無(wú)論是爬取網(wǎng)頁(yè)內(nèi)容還是進(jìn)行數(shù)據(jù)挖掘分析,C#都是一個(gè)強(qiáng)大而靈活的工具。希望本文能對(duì)讀者在這方面有所幫助。
、location.href常見(jiàn)的幾種形式
①如果頁(yè)面中自定義了frame,那么可將parent、self、top換為自定義frame的名稱,效果是在frame窗口打開(kāi)url地址。
②此外,window.location.href=window.location.href;和window.location.Reload();都是刷新當(dāng)前頁(yè)面。區(qū)別在于是否有提交數(shù)據(jù)。當(dāng)有提交數(shù)據(jù)時(shí),window.location.Reload()會(huì)提示是否提交,window.location.href=window.location.href;則是向指定的url提交數(shù)據(jù).
③用window.open()打開(kāi)新頁(yè)面但是用window.location.href=""卻是在原窗口打開(kāi)的.有時(shí)瀏覽器會(huì)一些安全設(shè)置window.open肯定被屏蔽。例如避免彈出廣告窗口。
二、location.href不同形式之間的區(qū)別
a.html:
b.html:
c.html:
d.html:
a.html里面嵌著b.html;b.html里面嵌著c.html;c.html里面嵌著d.html
在d.html里面head部分寫(xiě)js:
再次運(yùn)行a.html,點(diǎn)擊那個(gè)"跳轉(zhuǎn)"按鈕,運(yùn)行結(jié)果貼圖二如下:
對(duì)比圖一和圖二的變化,你會(huì)發(fā)現(xiàn)d.html部分已經(jīng)跳轉(zhuǎn)到了百度的首頁(yè),而其它地方?jīng)]有發(fā)生變化。這也就解釋了"本頁(yè)跳轉(zhuǎn)"是什么意思。
修改d.html里面的js部分為:
分析:我點(diǎn)擊的是a.html中嵌套的d.html部分的跳轉(zhuǎn)按鈕,結(jié)果是a.html中嵌套的c.html部分跳轉(zhuǎn)到了百度首頁(yè),這就解釋了"parent.location.href是上一層頁(yè)面跳轉(zhuǎn)"的意思。再次修改d.html里面的js部分為:
運(yùn)行a.html后,再次點(diǎn)擊"跳轉(zhuǎn)"按鈕,
你會(huì)發(fā)現(xiàn),a.html已經(jīng)跳轉(zhuǎn)到了百度首頁(yè)。
分析:我點(diǎn)擊的是a.html中嵌套的d.html部分的跳轉(zhuǎn)按鈕,結(jié)果是a.html中跳轉(zhuǎn)到了百度首頁(yè),這就解釋了"top.location.href是最外層的頁(yè)面跳轉(zhuǎn)"的意思。
三、location.href總結(jié)
看完上面的講解之后,在來(lái)看看下面的定義你就會(huì)非常明白了:
location是window對(duì)象的屬性,而所有的網(wǎng)頁(yè)下的對(duì)象都是屬于window作用域鏈中(這是頂級(jí)作用域),所以使用時(shí)是可以省略window。而top是指向頂級(jí)窗口對(duì)象,parent是指向父級(jí)窗口對(duì)象。
四、window.location.href和window.open的區(qū)別
window.location是window對(duì)象的屬性,而window.open是window對(duì)象的方法window.location是你對(duì)當(dāng)前瀏覽器窗口的URL地址對(duì)象的參考!window.open是用來(lái)打開(kāi)一個(gè)新窗口的函數(shù)!
在給按鈕、表格、單元格、下拉列表和DIV等做鏈接時(shí)一般都要用Javascript來(lái)完成,和做普通鏈接一樣,可能我們需要讓鏈接頁(yè)面在當(dāng)前窗口打開(kāi),也可能需要在新窗口打開(kāi),這時(shí)我們就可以使用下面兩項(xiàng)之一來(lái)完成:
window.location或window.open如何指定target?這是一個(gè)經(jīng)常遇到的問(wèn)題,特別是在用frame框架的時(shí)候解決辦法:
或
5、window.open用來(lái)打開(kāi)新窗口window.location用來(lái)替換當(dāng)前頁(yè),也就是重新定位當(dāng)前頁(yè)
用戶不能改變document.location(因?yàn)檫@是當(dāng)前顯示文檔的位置)。window.location本身也是一個(gè)對(duì)象。
但是,可以用window.location改變當(dāng)前文檔(用其它文檔取代當(dāng)前文檔),而document.location不是對(duì)象。服務(wù)器重定向后有可能使document.url變動(dòng),但window.location.href指的永遠(yuǎn)是訪問(wèn)該網(wǎng)頁(yè)時(shí)用的URL.大多數(shù)情況下,document.location和location.href是相同的,但是,當(dāng)存在服務(wù)器重定向時(shí),document.location包含的是已經(jīng)裝載的URL,而location.href包含的則是原始請(qǐng)求的文檔的URL.
6、window.open()是可以在一個(gè)網(wǎng)站上打開(kāi)另外的一個(gè)網(wǎng)站的地址window.location()是只能在一個(gè)網(wǎng)站中打開(kāi)本網(wǎng)站的網(wǎng)頁(yè)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持小編。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。