2026-01-06

Playwright截圖與錄屏功能深度教程

作為現(xiàn)代自動化測試的重要工具,Playwright的截圖和錄屏功能遠(yuǎn)比表面看上去強(qiáng)大。我在多個實際項目中深入使用這些功能后,發(fā)現(xiàn)了許多官方文檔未詳述的技巧和最佳實踐,今天就來分享這些經(jīng)驗。

一、基礎(chǔ)截圖功能:不只是page.screenshot()

大多數(shù)開發(fā)者都熟悉基礎(chǔ)截圖,但細(xì)節(jié)決定成敗:

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left; visibility: visible;">const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); // 基礎(chǔ)全屏截圖 await page.goto('https://example.com'); await page.screenshot({ path: 'screenshot.png' }); // 推薦:添加等待確保頁面穩(wěn)定 await page.waitForLoadState('networkidle'); // 等待網(wǎng)絡(luò)空閑 await page.waitForSelector('[#main](javascript:;)-content'); // 等待關(guān)鍵元素 await page.screenshot({ path: 'stable-screenshot.png', fullPage: false, // 默認(rèn)只截可視區(qū)域 animations: 'disabled'// 禁用動畫,避免截圖模糊 }); await browser.close(); })(); </pre>

關(guān)鍵點:直接截圖常遇到元素未加載或動畫干擾的問題。我習(xí)慣在截圖前至少等待networkidle狀態(tài),對于動態(tài)內(nèi)容多的頁面,還會添加額外的等待條件。

二、精準(zhǔn)元素截圖:定位的藝術(shù)

精確截圖特定元素是調(diào)試和測試的關(guān)鍵:

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">// 獲取元素并截圖 const element = await page.$('[#login](javascript:;)-form'); await element.screenshot({ path: 'login-form.png', // 添加邊框便于識別 style: 'border: 2px solid [#f00](javascript:;);' }); // 多個相同元素處理 const buttons = await page.$('button.submit-btn'); for (let i = 0; i < buttons.length; i++) { await buttons[i].screenshot({ path:button-{i}.png`, // 設(shè)置內(nèi)邊距,讓截圖更美觀 padding: { top: 5, right: 5, bottom: 5, left: 5 } }); } // 處理動態(tài)內(nèi)容:等待元素特定狀態(tài) await page.waitForSelector('.notification', { state: 'visible', timeout: 5000 }); const notification = await page.('.notification'); await notification.screenshot({ path: 'notification.png' });` </pre>

經(jīng)驗之談:元素截圖常因定位問題失敗。我總使用try-catch包裝截圖操作,并添加重試邏輯。對于絕對定位或fixed定位的元素,可能需要調(diào)整視口位置才能正確截圖。

三、全頁面滾動截圖:處理長頁面的挑戰(zhàn)

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">// 基礎(chǔ)全頁面截圖 await page.screenshot({ path: 'fullpage.png', fullPage: true, // 關(guān)鍵:設(shè)置高質(zhì)量截圖 quality: 100, // 僅限png,jpeg需要調(diào)整quality type: 'png' // png支持透明背景 }); // 處理懶加載頁面的技巧 asyncfunction screenshotLazyLoadPage(page, scrollDelay = 300) { // 先獲取頁面總高度 const bodyHeight = await page.evaluate(() =>document.body.scrollHeight); const viewportHeight = page.viewportSize().height; // 逐步滾動并觸發(fā)懶加載 for (let scrollTop = 0; scrollTop < bodyHeight; scrollTop += viewportHeight) { await page.evaluate((scrollTop) => { window.scrollTo(0, scrollTop); }, scrollTop); // 等待可能的懶加載 await page.waitForTimeout(scrollDelay); } // 回到頂部后截圖 await page.evaluate(() =>window.scrollTo(0, 0)); returnawait page.screenshot({ path: 'lazy-loaded-fullpage.png', fullPage: true }); } </pre>

踩坑提醒:全頁截圖時,fixed定位的導(dǎo)航欄或頁腳會在每屏重復(fù)出現(xiàn)。如果不需要這些重復(fù)元素,可以通過注入CSS臨時隱藏:await page.addStyleTag({ content: 'header { display: none !important; }' })

四、視口控制與多分辨率測試

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">// 測試不同設(shè)備截圖 const devices = [ { name: 'iPhone 12', viewport: { width: 390, height: 844 } }, { name: 'iPad', viewport: { width: 768, height: 1024 } }, { name: 'Desktop', viewport: { width: 1920, height: 1080 } } ]; for (const device of devices) { await page.setViewportSize(device.viewport); await page.waitForTimeout(1000); // 讓布局重新計算 await page.screenshot({ path:screenshot-${device.name}.png, // 針對移動端優(yōu)化 scale: device.name.includes('iPhone') ? 'css' : 'device' }); } // 截圖特定區(qū)域(視口部分區(qū)域) await page.screenshot({ path: 'viewport-area.png', clip: { x: 100, y: 100, width: 800, height: 600 } }); </pre>

五、錄屏功能:不僅僅是錄制

Playwright的錄屏功能在排查偶現(xiàn)問題時特別有用:

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">const { chromium } = require('playwright'); const fs = require('fs'); (async () => { const browser = await chromium.launch(); const context = await browser.newContext({ // 啟用錄屏 recordVideo: { dir: 'videos/', size: { width: 1280, height: 720 } } }); const page = await context.newPage(); // 開始錄制 await page.goto('https://example.com'); // 執(zhí)行一些操作 await page.click('[#login](javascript:;)'); await page.fill('[#username](javascript:;)', 'testuser'); await page.fill('[#password](javascript:;)', 'password123'); await page.click('[#submit](javascript:;)'); // 等待重要操作完成 await page.waitForNavigation(); // 關(guān)閉context會自動保存視頻 await context.close(); // 獲取視頻路徑 const videoPath = await page.video().path(); console.log(視頻已保存到: ${videoPath}); await browser.close(); })(); </pre>

六、高級技巧與實戰(zhàn)經(jīng)驗

1. 截圖對比測試

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">const PixelDiff = require('pixel-diff'); asyncfunction compareScreenshots(page, baselinePath, diffPath) { const currentPath = 'current.png'; await page.screenshot({ path: currentPath }); const diff = new PixelDiff({ imageAPath: baselinePath, imageBPath: currentPath, imageOutputPath: diffPath, threshold: 0.1, // 允許的差異閾值 }); const result = await diff.run(); return result.differences < result.threshold; // 是否通過 } </pre>

2. 截圖失敗自動重試

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">async function robustScreenshot(page, options, maxRetries = 3) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { await page.waitForTimeout(500 * attempt); // 遞增等待 returnawait page.screenshot(options); } catch (error) { lastError = error; console.log(截圖嘗試 {attempt} 失敗:{error.message}); if (attempt === maxRetries) { // 最后嘗試:調(diào)整視口大小 await page.setViewportSize({ width: 1280, height: 800 }); await page.waitForTimeout(1000); returnawait page.screenshot(options); } } } throw lastError; } </pre>

3. 優(yōu)化錄屏文件大小

<pre data-tool="mdnice編輯器" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">const context = await browser.newContext({ recordVideo: { dir: 'videos/', size: { width: 1280, height: 720 }, // 降低幀率減少文件大小 fps: 15, } }); // 只錄制關(guān)鍵測試部分 await page.startRecording({ path: 'critical-path.webm' }); // ...關(guān)鍵操作 await page.stopRecording(); </pre>

七、性能考量與最佳實踐

  1. 內(nèi)存管理:長時間運行截圖任務(wù)時,定期清理緩存:

    <pre style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">await page.evaluate(() => { if (window.gc) window.gc(); // 僅限Chrome }); </pre>

  2. 并行處理:同時運行多個截圖任務(wù)時,限制并發(fā)數(shù)避免內(nèi)存溢出。

  3. 存儲優(yōu)化

  • 使用JPEG格式減小文件大?。?code>{ type: 'jpeg', quality: 80 }
  • 定期清理舊的截圖和視頻文件
  • 考慮將文件上傳到云存儲
  1. 錯誤處理完整示例

    <pre style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left;">async function safeCapture(page, url, outputPath) { try { await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); await page.waitForSelector('body', { state: 'attached' }); // 檢查頁面是否正常加載 const pageTitle = await page.title(); if (!pageTitle || pageTitle.includes('Error')) { thrownewError(頁面加載異常: {pageTitle}`); } returnawait page.screenshot({ path: outputPath }); } catch (error) { // 保存錯誤狀態(tài)截圖 const errorPath = outputPath.replace('.png', '-error.png'); await page.screenshot({ path: errorPath }); console.error(`截圖失敗{url}:, error); throw error; } } </pre>

結(jié)語

Playwright的截圖和錄屏功能看似簡單,但在實際項目中需要綜合考慮性能、穩(wěn)定性和可維護(hù)性。我建議根據(jù)具體場景選擇合適的策略:對于CI/CD環(huán)境,優(yōu)先考慮速度和穩(wěn)定性;對于調(diào)試和演示,則側(cè)重功能的完整性和易用性。

最重要的是,建立適合自己項目的截圖規(guī)范,包括命名規(guī)則、存儲結(jié)構(gòu)和比對機(jī)制,這樣才能讓這些功能真正提升開發(fā)效率,而不是成為維護(hù)負(fù)擔(dān)。記住,好的工具使用習(xí)慣往往比工具本身更重要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容