:無頭瀏覽器自動化截圖與爬蟲)
簡介這是面向Windows 64位平臺的Chrome無頭瀏覽器可執(zhí)行包版本為129.0.6668.59適用于需要在不渲染圖形界面的環(huán)境中運行瀏覽器自動化任務的開發(fā)者、測試工程師及CI/CD集成場景。通過搭配ChromeDriver可完成頁面訪問、元素操作、Cookie讀取等Web自動化測試工作尤其適合服務端后臺任務或資源受限環(huán)境。壓縮包共125個文件主要包含headless_shell主程序及exe可執(zhí)行文件、dll動態(tài)鏈接庫、pak資源文件、hyb二進制數(shù)據(jù)、以及json、js、dat等配置與快照文件整體大小約100.37MB結構清晰可直接解壓使用。目前已有397人學習下載。該版本與Chrome 129.0.6668.59功能對齊解壓后即為可運行的headless shell便于開發(fā)者快速搭建無頭瀏覽器環(huán)境、編寫自動化腳本或集成到測試框架中同時也有助于理解Chrome無頭模式的組件構成與運行依賴。 前一陣幫客戶做數(shù)據(jù)平臺的定時報表截圖需要在Windows Server上跑一個無頭瀏覽器每天生成幾十張頁面截圖再拼成郵件附件。一開始圖省事直接裝了完整版Chrome用--headlessnew跑結果機器上殘留一堆進程內存占用還高得離譜。后來翻到Chrome官方分發(fā)的chrome-headless-shell-win64-129.0.6668.59這類獨立二進制包才發(fā)現(xiàn)這個專門給自動化場景用的瘦身瀏覽器才是對口的工具。這篇文章我把從下載、命令行驗證、接入自動化框架到真實項目落地的完整過程都寫出來包括我在Windows上踩過的坑希望對剛接觸無頭瀏覽器的朋友有幫助。1. 為什么Chrome團隊要單獨拆一個headless shell出來1.1 從舊無頭模式到現(xiàn)在的產物線Chrome做無頭模式不是一天兩天了早年是在完整Chrome里加一個--headless開關跑但這個舊實現(xiàn)和用戶實際用的渲染行為有不少差異自動化腳本里經(jīng)常出現(xiàn)本地正常、無頭環(huán)境就變樣的問題。Chrome 112以后官方把無頭模式的重心切換到--headlessnew行為和完整Chrome幾乎一致??蓡栴}跟著來了自動化任務真正需要的只是能解析HTML、執(zhí)行JS、渲染出結果這部分能力根本用不上下載管理器、系統(tǒng)托盤圖標、自動更新服務這些拖油瓶。Chrome團隊于是維護了一條獨立的產物線叫Chrome for Testing里面專門有一個chrome-headless-shell和主版本號同步發(fā)布。129.0.6668.59對應的是Chrome 129內核2024年下半年發(fā)布的版本win64后綴代表Windows 64位。拿它做爬蟲、自動化測試、報表截圖屬于真正的對癥下藥。1.2 它和完整Chrome的真實差距我實際對比過chrome-headless-shell壓縮包大概一百多兆解壓后也遠小于完整Chrome的安裝目錄里面沒有Google Update、沒有后臺服務、沒有默認瀏覽器注冊邏輯就是一個可以直接丟到服務器上跑的可執(zhí)行文件加一堆配套DLL。啟動后的進程樹也更清爽完整Chrome打開一個標簽往往會拉起渲染進程、GPU進程、網(wǎng)絡進程一大家子這個shell在簡單頁面下明顯更瘦。代價也直白不支持瀏覽器擴展不提供下載管理很多面向消費者的功能被砍掉了。但對自動化來說這些恰恰是噪音。做這行最怕的就是功能沒用到資源全被吃掉。2. 在Windows上把環(huán)境搭起來下載、解壓與首次驗證2.1 去哪里下載、選哪個版本下載不要走第三方站點直接看Chrome for Testing的官方渠道。Google維護了一個last-known-good-versions.json接口里面會給出版本號和對應的下載地址也可以直接把URL里的版本號替換成129.0.6668.59下載對應平臺的zip包。下載后解壓找到目錄里的chrome-headless-shell.exe這就是核心了。關于版本選擇我的建議是固定版本目錄比如C:\tools\chrome-headless-shell-129\。不要解壓到桌面更不要放到帶空格的用戶目錄下。Windows環(huán)境下很多自動化工具對路徑里的空格和中文支持不好這個我后面還會細說總之盡量讓路徑保持全英文、無空格、無特殊符號。2.2 命令行第一次跑通打開PowerShell或cmd先跑一個最簡單的命令驗證可執(zhí)行文件是否正常C:\tools\chrome-headless-shell-129\chrome-headless-shell.exe --headless --disable-gpu --dump-dom https://example.com正常情況會把目標頁面的DOM直接打到標準輸出。--dump-dom是我拿到新版本后第一個跑的參數(shù)用來驗證能不能正常工作。然后再試截圖chrome-headless-shell.exe --headless --disable-gpu --screenshotC:\tmp\page.png --window-size1280,800 https://example.com有個細節(jié)--screenshot后面要跟完整的Windows路徑如果目錄不存在它也不報錯就是不出圖。排查時先確認目錄存在、路徑拼接正確別一上來就懷疑瀏覽器壞了。2.3 幾個高頻啟動參數(shù)--user-data-dir指定用戶數(shù)據(jù)目錄。不加的話每次啟動都用臨時目錄Cookie和登錄態(tài)存不住。做需要登錄的自動化這個參數(shù)必須給。--no-sandboxWindows上一般不強制但某些遠程桌面環(huán)境和終端服務環(huán)境下不加上可能啟動失敗報錯信息還特別讓人摸不著頭腦。--virtual-time-budget虛擬時間預算。讓瀏覽器在虛擬時間里加速等待指定毫秒數(shù)對SPA頁面截圖特別有用后面細說。--remote-debugging-port打開調試端口配合CDP協(xié)議做精細控制。--disable-gpu服務器上基本沒有GPU可用顯式關閉能避免一部分兼容性警告。3. 接入自動化框架Puppeteer、CDP直連與Selenium的取舍3.1 Puppeteer指定可執(zhí)行文件用puppeteer-core是最干凈的方式它不會自動下載瀏覽器路徑完全由你控制const puppeteer require(puppeteer-core); const browser await puppeteer.launch({ executablePath: C:\\tools\\chrome-headless-shell-129\\chrome-headless-shell.exe, headless: true, args: [--disable-gpu, --no-first-run] }); const page await browser.newPage(); await page.goto(https://example.com, { waitUntil: networkidle2 }); await page.screenshot({ path: C:\\tmp\\page.png, fullPage: true }); await browser.close();這里有個容易混淆的點不帶-core的puppeteer包默認會在自己緩存目錄里下載并管理Chrome和chrome-headless-shell版本管起來很繞。干脆用puppeteer-core加顯式executablePath整個行為就完全透明了。出問題的時候你能確定用的是哪個瀏覽器這是一個很大的排查優(yōu)勢。3.2 不引入框架直接用CDP協(xié)議控制如果不想為一個小功能引入整個Node依賴走CDP協(xié)議是最直接的。啟動時開調試端口chrome-headless-shell.exe --headless --disable-gpu --remote-debugging-port9222 --user-data-dirC:\tmp\cdp-profile about:blank然后訪問http://127.0.0.1:9222/json/version拿到webSocketDebuggerUrl用任何支持WebSocket的語言連上去發(fā)Page.navigate、Runtime.evaluate、Page.captureScreenshot這些命令。我在寫跨語言定時任務時經(jīng)常用這條路徑因為不綁定某個SDK以后換語言接入成本最低。3.3 Selenium的搭配建議如果你項目里已經(jīng)有一堆Selenium用例也可以把ChromeDriver指向這個shell來跑但要注意ChromeDriver的版本必須和Chrome 129這條線匹配否則握手階段就會報錯。我的個人意見是Selenium的老用戶別折騰遷移直接用完整Chrome加ChromeDriver更省心新項目、腳本化的任務才建議用Puppeteer或CDP配這個shell。工具沒有絕對優(yōu)劣只有適不適合當前場景。4. 真實項目里的幾種打開方式4.1 大批量頁面截圖巡檢給內部系統(tǒng)做每日巡檢幾十個頁面挨個截圖是我用得最多的場景。啟動一個瀏覽器實例按清單依次訪問關鍵在于控制每個頁面的等待時間和超時。shell模式下頁面加載失敗不會自己退出腳本必須給每個頁面加超時保護否則一個壞頁面就能讓整個巡檢任務卡死到第二天早上。我的做法是每個頁面單獨try-catch失敗就把URL記錄下來最后統(tǒng)一生成一份巡檢報告而不是中斷整個流程。4.2 抓取JS渲染的數(shù)據(jù)很多網(wǎng)站的正文數(shù)據(jù)是前端通過接口異步加載的直接用curl拿HTML什么都拿不到。這種場景shell可以頂上訪問頁面、等數(shù)據(jù)渲染、再抓取DOM。配合--virtual-time-budget5000讓頁面在虛擬時間里等5秒真實耗時可能只有一兩秒比傻等setTimeout效率高得多。有意思的是這種虛擬時間機制還能讓一些懶加載圖片提前加載完成截圖或抓取時不容易出現(xiàn)空白區(qū)域。4.3 自動化測試的冒煙回歸在CI里跑冒煙測試shell的價值在于啟動快、進程干凈。一個檢查腳本就能干這個事跑--dump-dom再用字符串匹配或正則判斷關鍵內容是否出現(xiàn)。不需要完整的斷言框架一條命令就能確認核心頁面沒掛。對發(fā)布前檢查這類需求這種輕量確認往往比大而全的端到端測試更實用。4.4 Windows計劃任務里的一條龍在Windows Server上最省事的做法是直接用任務計劃程序每天早上調用一個腳本訪問幾個報表頁面、生成截圖、調用郵件發(fā)送。整個鏈路不需要安裝任何語言運行時核心步驟就是一條shell命令加一個PowerShell腳本。實測跑了好幾個月穩(wěn)定性完全可以接受。5. 實測中踩過的坑與排查思路5.1 進程不退出怎么處理命令行方式執(zhí)行完截圖后有時候進程不會自動退出這在某些版本上是個老毛病。我寫腳本時最后都會加一層兜底清理taskkill /F /IM chrome-headless-shell.exe如果用了Puppeteer則在Node側用try-finally保證browser.close()一定執(zhí)行。但腳本中途拋異常時進程仍可能殘留所以外層定時任務里再加一次進程清理是必須的。寧可錯殺不能讓僵尸進程越積越多把服務器內存吃光。5.2 Windows Server上的中文字體問題這是個特別隱蔽的坑。服務器沒裝中文字體時頁面渲染看起來正常截圖也成功但里面中文全部是方塊。我第一次遇到以為是頁面樣式問題折騰了半天才發(fā)現(xiàn)是系統(tǒng)字體缺失。解決辦法很直接給Windows Server安裝微軟雅黑等中文字體或者把開發(fā)機上的字體文件復制過去重啟后刷新字體緩存。5.3 用戶數(shù)據(jù)目錄沖突多個shell實例共用同一個--user-data-dir時后啟動的實例會直接失敗因為瀏覽器認為已有實例在運行。并行任務必須給每個實例分配獨立目錄或者在啟動腳本里生成隨機臨時目錄。這個問題多人協(xié)作時尤其容易犯一個人寫的腳本在本地跑沒事放到CI上多任務并行就崩了。5.4 版本匹配引發(fā)的靜默失效chrome-headless-shell的版本和ChromeDriver、Puppeteer等三方庫的版本必須大致匹配。129.0.6668.59 這個版本相對較新如果配套Node庫版本太舊會出現(xiàn)啟動正常但執(zhí)行某些命令沒反應的詭異現(xiàn)象。遇到這種情況先查框架的版本支持文檔不要把時間浪費在排查業(yè)務代碼上。5.5 閃退問題的排查手段如果shell啟動就閃退用下面這條命令把日志打出來chrome-headless-shell.exe --headless --enable-loggingstderr --v1 --dump-dom https://example.com能直接看到是缺DLL、缺依賴還是參數(shù)沖突。另外開了--remote-debugging-port后用瀏覽器訪問/json/version也可以確認調試服務是否正常起來。6. 性能調優(yōu)與資源控制經(jīng)驗6.1 多實例并發(fā)時的內存壓測shell多開時每個實例都有獨立的渲染進程和網(wǎng)絡進程內存占用大約是完整Chrome的六成左右但開多了依然扛不住。實測壓下來--renderer-process-limit1和--utility-process-limit這兩個參數(shù)配合使用能把并行任務的內存峰值壓下去不少。注意這兩個參數(shù)在Windows上的表現(xiàn)和Linux略有差異最好壓測后在當前機器上驗證一遍。6.2 頁面加載提速的實用參數(shù)只關心文字內容或數(shù)據(jù)結構時可以禁用圖片加載--blink-settingsimagesEnabledfalse大圖多的頁面提速特別明顯。還有--js-flags--max-old-space-size512限制JS堆內存防止某個極端頁面把整個機器拖死。對于定時任務這種長期運行的程序這類資源兜底參數(shù)比業(yè)務邏輯本身更重要。6.3 常用參數(shù)組合速查場景推薦參數(shù)快速驗證--headless --dump-dom URL頁面截圖--headless --screenshot路徑 --window-size寬,高 URLSPA等待渲染--headless --virtual-time-budget5000 --dump-dom URL多實例并發(fā)--headless --user-data-dir獨立目錄 --disable-gpu --renderer-process-limit1CDP精細控制--headless --remote-debugging-port9222最后分享一個我自己的習慣所有版本的chrome-headless-shell都統(tǒng)一放在一個目錄下目錄名就是版本號絕不叫最新或old這類模糊名字。每次Chrome發(fā)大版本就去官方JSON接口拿新鏈接下載后先命令行驗證一遍再改自動化腳本里的exe路徑。這樣即使新版本出問題也能立刻回退到上一個版本整個過程不需要去翻當初裝的是哪個版本這種記憶。自動化工具這東西版本可控心里就不慌。本文還有配套的精品資源點擊獲取