大資料時代,各行各業對資料采集的需求日益增多,網絡爬蟲的運用也更為廣泛,越來越多的人開始學習網絡爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實作從易到難全方位覆寫,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為自動化工具 playwright 的使用。
概述
上期文章中講到了自動化工具 Selenium 的基本使用方法,也介紹了 Selenium 的優缺點。Selenium的功能非常強大,支援所有現代浏覽器。但是 Selenium 使用起來十分不友善,我們需要提前安裝好浏覽器,然後下載下傳對應版本的驅動檔案,當浏覽器更新後驅動檔案也得随之更新。如果想要大規模且長期的采集資料,那麼部署 Selenium 時環境配置會是一個大問題。是以本期我們将介紹一款更加好用的自動化工具 Playwright 。
Playwright 的使用
介紹
Playwright是一個用于自動化Web浏覽器測試和Web資料抓取的開源庫。它由Microsoft開發,支援Chrome、Firefox、Safari、Edge和WebKit浏覽器。Playwright的一個主要特點是它能夠在所有主要的作業系統(包括Windows、Linux和macOS)上運作,并且它提供了一些強大的功能,如跨浏覽器測試、支援無頭浏覽器、并行執行測試、元素截圖和模拟輸入等。它主要有以下優勢:
- 相容多個浏覽器,而且所有浏覽器都使用相同的API。
- 速度快、穩定性高,即使在大型、複雜的Web應用程式中也可以運作。
- 支援無頭浏覽器,是以可以在沒有可見界面的情況下運作測試,進而提高測試效率。
- 提供了豐富的 API,以便于執行各種操作,如截圖、模拟輸入、攔截網絡請求等。
安裝
使用 Playwright 需要 Python版本在3.7以上。
安裝 Playwright 可以直接使用 pip 工具:
pip install playwright
安裝完成後需要進行初始化操作,安裝所需的浏覽器。
playwright install
執行上述指令時,Playwright 會自動安裝多個浏覽器(Chromium、Firefox 和 WebKit)并配置驅動,是以速度較慢。
使用
Playwright 支援同步與異步兩種模式,這裡分開來進行講解。
同步
使用 Playwright 時可以選擇啟動安裝的三種浏覽器(Chromium、Firefox 和 WebKit)中的一種。
from playwright.sync_api import sync_playwright
# 調用sync_playwright方法,傳回浏覽器上下文管理器
with sync_playwright() as p:
# 建立谷歌浏覽器示例,playwright預設啟動無頭模式,設定headless=False,即關閉無頭模式
browser = p.chromium.launch(headless=False)
# 建立頁籤
page = browser.new_page()
# 跳轉到目标網址
page.goto("http://baidu.com")
# 擷取頁面截圖
page.screenshot(path='example.png')
# 列印頁面的标題,也就是title節點中的文本資訊
print(page.title())
# 關閉浏覽器
browser.close()
# 輸出:百度一下,你就知道
可以看到,Playwright 的使用也比較簡單,文法比較簡潔,而且浏覽器的啟動速度以及運作速度也很快。
異步
異步代碼的編寫方法與同步基本一緻,差別在于同步調用的是 sync_playwright,異步調用的是 async_playwright。最終運作效果與同步一緻。
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")
# 列印網頁源代碼
print(await page.content())
await browser.close()
asyncio.run(main())
代碼生成
Playwright 提供了代碼生成功能,這個功能可以對我們在浏覽器上的操作進行錄制并生成代碼,它可以有效提高程式的編寫效率。代碼生成功能需要使用 Playwright 指令行中的 codegen實作,codegen 指令存在如下主要參數:
-o :将生成的腳本儲存到指定檔案
--target :生成的語言,預設為 Python
--save-trace :記錄會話的跟蹤并将其儲存到檔案中
-b :要使用的浏覽器,預設為 chromium
--timeout :設定頁面加載的逾時時間
--user-agent :指定UA
--viewport-size :指定浏覽器視窗大小
我們在指令行執行指令:playwright codegen -o script.py
執行指令後會彈出一個 chromium 浏覽器與腳本視窗,當我們在浏覽器上進行操作時,腳本視窗會根據我們的操作生成對應代碼。當我們操作結束後,關閉浏覽器,在目前目錄下會生成一個 script.py 檔案,該檔案中就是我們在進行浏覽器操作時,Playwright 錄制的代碼。我們運作該檔案,就會發現它在複現我們之前的操作。
代碼生成功能的實用性其實較為一般,它隻能實作比較簡單的操作,當遇到複雜操作時,生成的代碼就容易出現問題。最好的方式是使用代碼生成功能生成部分操作的代碼,然後再手動去修改它生成的代碼。
隔離
上一步中,我們使用代碼生成功能生成了一段代碼,我們會發現這段代碼中使用到了一個 new_context 方法,通過這個方法建立了一個 content ,然後再去進行其它操作。這個 new_content 方法其實是為了建立一個獨立的全新上下文環境,它的目的是為了防止多個測試用例并行時各個用例間不受幹擾,當一個測試用例異常時不會影響到另一個。
browser = playwright.chromium.launch()
context = browser.new_context()
page = context.new_page()
定位器
Playwright 提供了多種定位器來幫助開發中定位元素。
page.get_by_role() :通過顯式和隐式可通路性屬性進行定位。
page.get_by_text() :通過文本内容定位。
page.get_by_label() :通過關聯标簽的文本定位表單控件。
page.get_by_placeholder() :按占位符定位輸入。
page.get_by_alt_text() :通過替代文本定位元素,通常是圖像。
page.get_by_title() :通過标題屬性定位元素。
page.get_by_test_id() :根據data-testid屬性定位元素(可以配置其他屬性)。
page.locator():拓展選擇器,可以使用 CSS 選擇器進行定位
使用定位器最好的方式就是上文中講到的利用代碼生成功能來生成定位代碼,然後手動去修改,這裡就不做嘗試。
選擇器
Playwright 支援 CSS、Xpath 和一些拓展選擇器,提供了一些比較友善的使用規則。
CSS 選擇器
# 比對 button 标簽
page.locator('button').click()
# 根據 id 比對,比對 id 為 container 的節點
page.locator('#container').click()
# CSS僞類比對,比對可見的 button 按鈕
page.locator("button:visible").click()
# :has-text 比對任意内部包含指定文本的節點
page.locator(':has-text("Playwright")').click()
# 比對 article 标簽内包含 products 文本的節點
page.locator('article:has-text("products")').click()
# 比對 article 标簽下包含類名為 promo 的 div 标簽的節點
page.locator("article:has(div.promo)").click()
Xpath
page.locator("xpath=//button").click()
page.locator('xpath=//div[@class="container"]').click()
其它
# 根據文本比對,比對文本内容包含 name 的節點
page.locator('text=name').click()
# 比對文本内容為 name 的節點
page.locator("text='name'").click()
# 正則比對
page.locator("text=/name\s\w+word").click()
# 比對第一個 button 按鈕
page.locator("button").locator("nth=0").click()
# 比對第二個 button 按鈕
page.locator("button").locator("nth=-1").click()
# 比對 id 為 name 的元素
page.locator('id=name')
等待
當進行 click 、fill 等操作時,Playwright 在采取行動之前會對元素執行一系列可操作性檢測,以確定這些行動能夠按預期進行。
如對元素進行 click 操作之前,Playwright 将確定:
元素附加到 DOM
元素可見
元素是穩定的,因為沒有動畫或完成動畫
元素接收事件,因為沒有被其他元素遮擋
元素已啟用
即使 Playwright 已經做了充分準備,但是也并不完全穩定,在實際項目中依舊容易出現因頁面加載導緻事件沒有生效等問題,為了避免這些問題,需要自行設定等待。
# 固定等待1秒
page.wait_for_timeout(1000)
# 等待事件
page.wait_for_event(event)
# 等待加載狀态
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()
# 添加事件 發起請求時列印URL
page.on("request", print_request_sent)
# 請求完成時列印URL
page.on("requestfinished", print_request_finished)
page.goto("https://baidu.com")
# 删除事件
page.remove_listener("requestfinished", print_request_finished)
browser.close()
反檢測
在 Selenium 的使用中,我們講到了自動化工具容易被網站檢測,也提供了一些繞過檢測的方案。這裡我們介紹一下 Playwright 的反檢測方案。
以 https://bot.sannysoft.com/ 為例,我們分别測試正常模式與無頭模式下的檢測結果。
正常模式:
無頭模式:
可以看到,正常模式下 WebDriver 一欄報紅,而無頭模式下更是慘不忍睹,基本上所有特征都被檢測到了。這些還隻是最基本的檢測機制,自動化工具的弱點就暴露的很明顯了。
與 Selenium 一樣,繞過檢測主要還是針對網站的檢測機制來處理,主要就是在頁面加載之前通過執行 JS 代碼來修改一些浏覽器特征。以無頭模式為例:
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'
)
# 執行 JS 代碼
page.add_init_script(js)
page.goto("https://bot.sannysoft.com/")
page.screenshot(path='example.png')
browser.close()
這裡與 Selenium 反檢測方案一樣,執行 stealth.min.js 來隐藏特征( stealth.min.js 的來源與介紹參考上期文章)。最終結果如下圖:
可以看到,與真實浏覽器通路基本一緻了。
總結
與 Selenium 相比,Playwright 最大的優點就是不需要手動安裝驅動,而且它擁有更好的性能與更多的功能。是以 在爬蟲領域,Playwright 是更好的選擇。