大資料時代,各行各業對資料採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,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 是更好的選擇。