Puppeteer 是 Google 推出的 Node 函式庫,可直接用 JavaScript 操控真實的 Chrome——你負責寫自動化邏輯,Chrome DevTools Protocol 負責傳遞指令,而完整瀏覽器會先把頁面渲染出來,之後你再去讀取內容。它在 GitHub 上的專案是 puppeteer/puppeteer:採用 Apache-2.0 授權、以 TypeScript 撰寫,星標數大約 95.3k(我抓快照當天是 95,307)。官方定位也刻意收得很窄——「一個用來控制 Chrome(以及實驗性支援 Firefox)的 JavaScript API」——這句話不只告訴你它是什麼,也同樣清楚說明它不是什麼。
我用和每個瀏覽器自動化函式庫相同的測試環境與公開示範站,實測了 Puppeteer 24.16.0:包含有分頁的靜態型錄、文章頁、JavaScript 動態型錄、JSON API、回傳 500 的路由、一張小型爬取圖,以及 Books to Scrape 和 Quotes to Scrape。它的渲染表現乾淨俐落,幾乎沒什麼多餘動作。只是它也把同樣一個任務留在我桌上——也就是每個無頭瀏覽器函式庫都會留下的那個任務——而老實面對這個缺口,正是區分「有用的評測」與「公關稿」的關鍵。
有一個數字比召回率更引人注意。在一個資料來自 JSON 端點的頁面上,Puppeteer 連 DOM 都沒碰,就把全部 8 筆記錄抓了下來——它是在頁面內執行 fetch,直接讀取回應物件。再加上原生渲染與可正常使用的截圖,這就是這個工具的樣貌:一個成熟的 Chrome 渲染器,而不是爬蟲框架;這個差異在你寫下第一行程式前就很重要。
Puppeteer 到底是什麼(以及它的競品是誰)
在這裡,類別名稱真的很重要,所以先從它開始。Puppeteer 是一個瀏覽器自動化函式庫。它會啟動 Chrome、打開頁面、讓頁面中的 JavaScript 跑完,然後把渲染後的結果交給你讀取或截圖。這就是你會選它而不是 HTTP 客戶端加 HTML parser 的原因:你要的是伺服器先送出內容、腳本執行完之後的頁面,而不是一開始那個空殼。
從定位上看,它競爭的是其他真實瀏覽器函式庫——Playwright 和 Selenium——而不是 Scrapy 這類爬蟲框架,也不是 LLM-Markdown 工具。如果你把一千個 URL 丟給 Puppeteer,期待它幫你排隊、去重、禮貌限速,還順便把資料集寫好,那你其實是拿渲染器去做爬取問題。它會把每一頁漂亮地渲染出來,但不負責流程編排。(這不是缺陷,而是範圍邊界——我後面還會再提,因為在採用這個工具前,這是最需要先內化的一件事。)

Puppeteer 來自 Google 的 Chrome 團隊,所以它天生就是以 Chrome 為先,API 也像是把瀏覽器原生除錯協定套上一層薄而順手的手套。它已經夠成熟,成熟到有點「無聊但很好」:你需要的功能多年來都維持穩定,文件完整,周邊生態也很深。
原生渲染與頁內 fetch 模式
從結果表中有兩個行為特別值得拉出來看,因為它們決定了你實際會怎麼用這個工具。
第一,原生渲染。那個 JavaScript 動態型錄——頁面載入後才在前端組出商品網格——最後拿到 8/8,並且成功把整頁截圖存到磁碟。設定很簡單,但確實有在 goto 之後等待目標內容。公開的 Quotes to Scrape JS 頁也用同樣模式抓到全部 10 則引言。這些都是固定測試頁的結果,不是泛用的渲染召回率分數。
第二,JSON API 測試頁從 /api/dynamic-products 載入商品。透過 page.evaluate 在同源環境中執行 fetch,就直接拿到全部 8 筆資料,不必解析已渲染的列。這是一種通用的瀏覽器內評估模式,不是 Puppeteer 獨有的神奇發現功能。當端點與請求契約都已知時,它可以大幅簡化擷取;但授權標頭、執行期 token、credentials 政策、CORS/CSP、service worker,以及分頁機制,都可能讓應用程式實際請求與你想像的不一樣。
第三個要帶走的,不是某個數字,而是整體框架:Puppeteer 是成熟的 Chrome 渲染器,但它不是爬蟲。這兩件事都對,而第二件事正是很多介紹文會刻意略過的重點。
Puppeteer 如何跟 Chrome 溝通

在 Chrome 上,Puppeteer 使用 Chrome DevTools Protocol(CDP),也就是瀏覽器 DevTools 背後那條透過 WebSocket 傳 JSON 的通道。puppeteer.launch() 會啟動 Chrome 並建立這條協定連線;像 goto、$$eval、screenshot 這些呼叫,則是把瀏覽器操作包裝成更高層的 API。Firefox 的支援則走下面提到的 WebDriver BiDi 路線,所以不是每一個 Puppeteer 操作都對應成通用的 CDP 指令。
page.evaluate 會在頁面上下文中執行函式,因此像 fetch('/api/...') 這種相對路徑,會使用該頁面的 origin,且可能重用符合條件的 cookie 與 session 狀態。但它不會自動複製應用程式額外加入的授權標頭、請求選項、token 或 service worker 行為。在這個同源測試頁上,它可以直接回傳 JSON;但到了正式環境,就得先確認請求契約到底是什麼。
這也正是 Puppeteer 為什麼「重」。每個頁面都是真實瀏覽器分頁,背後有真實的渲染引擎。這讓它在 JavaScript 很重的頁面上更準確,但相較於純 HTTP 抓取,也意味著更多記憶體與啟動時間。渲染不是免費的;CDP 只是把帳單講得更清楚。
引擎支援這件事,直接說清楚
外界常把 Puppeteer 簡化成「只能跑 Chrome」。就我這次測試的版本來說,這說法不對,而釐清這點會直接改變比較方式。
| 引擎 | Puppeteer 24.16.0 的驅動方式 | 本次測試是否有實際測 |
|---|---|---|
| Chrome | 以 Chrome 為先,透過 CDP 驅動——預設路線,因此既有自動化流程仍可維持運作 | 是 |
| Firefox | 自 v23 起已文件化支援 WebDriver BiDi | 否 |
| WebKit | 完全不驅動 | — |
Chrome for Developers 和 Mozilla 都在 Firefox 支援落地時做過說明。我跑的版本是 24.16.0,早就超過 v23,所以「只能跑 Chrome」其實低估了它包裝箱內真正提供的能力。真正的廣度差異,是它沒有 WebKit,再加上跨引擎故事相較 Playwright 還比較年輕,而不是「一個引擎對三個引擎」。
Firefox/BiDi 的路徑在我測試的版本中確實已文件化且可用,但我沒有把固定測試頁也跑過一遍,所以我報告的是「能力」,不是「量測結果」。如果 Firefox 渲染對你的目標站點是硬需求,請先用你自己的頁面驗證後再決定。想看引擎與語言支援範圍的完整對照,可以參考我們的 Playwright 與 Puppeteer 比較;那篇會把兩個函式庫放進同一組測試裡,直接回答「該選哪個」這個問題。這篇則只聚焦 Puppeteer。
安裝與設定現實:最重的是瀏覽器本體
預設執行 npm install puppeteer 時,會下載相容的 Chrome for Testing 版本。這個行為可以透過設定跳過或改指向其他來源,使用者也可以把 Puppeteer 指到別的可執行檔,因此版本對齊會取決於你的部署選擇。這次安裝中,瀏覽器下載是最重的部分;而套件管理器的稽核快照,不應被視為永久性的安全屬性。
這種自動打包確實有很好的易用性,但也確實有實質體積成本,兩面都值得講清楚。好處是:你不用自己找相容瀏覽器,也不用手動鎖版本;npm install 直接給你可用的一組。代價是:你會下載整個瀏覽器,所以磁碟與頻寬都要預留,尤其在 CI 環境裡,冷快取每次都得重新付這筆帳。
這點也很明顯對比 Playwright:它把兩步驟拆開——先安裝函式庫,再執行 npx playwright install 另外抓瀏覽器建置版本。兩種方式都不算痛苦,只是踩坑的方式不同。Puppeteer 的單一指令,可能在有限流量環境下讓你意外地覺得「怎麼這麼大」;Playwright 的第二步,則可能因為你忘了跑而出問題。要知道你正在用哪一套流程。
實測結果

所有測試都在 127.0.0.1 的本機測試伺服器,以及兩個公開練習站上進行;執行環境為 Node v22.22.3、macOS arm64、Puppeteer 24.16.0,並使用其內建的 Chrome。每個測試頁的真值資料都在執行前先寫好,所以召回率是對照固定預期集來算,而不是看 Puppeteer 當下印了什麼。
公開研究包包含了 fixture server、test runner 與 ground truth。由於嚴格的安全審查拒絕了 dependency-lock 資料與依賴環境的端點素材,發佈包中刻意未附上鎖檔與原始執行摘要。若要重現安全的本機測試頁,請先執行 npm install,再在 tools/puppeteer/tests 內執行 node run_puppeteer_material_tests.mjs,然後把結果與已公開的真值比對。公開示範頁可能會變動,所以本機測試頁才是檢查預期數量的穩定基礎。
如果你想做精確的歷史相依性重建,請在本地建立並審核新的 lockfile,而不是把未公開的鎖檔當成公開證據。
| 測試 | 目標 | 結果 |
|---|---|---|
| 靜態型錄 + 分頁 | 本機測試頁 | 12/12,召回率 1.0 |
| 文章擷取 | 本機測試頁 | 標題 + 3/3 段落,成功分離樣板內容 |
| 動態 JS 頁(原生渲染) | 本機測試頁 | 8/8,召回率 1.0,已儲存整頁截圖 |
動態 JSON API(頁內 fetch) | 本機測試頁 | 8/8,召回率 1.0,未做 DOM 解析 |
| HTTP 500 處理 | 本機測試頁 | 可檢視 500 狀態,未拋出例外 |
| 爬取圖(手寫 BFS) | 本機測試頁 | 12 頁,深度 {0:1, 1:4, 2:7} |
| Books to Scrape | 公開示範頁 | 20 個商品 |
| Quotes JS | 公開示範頁 | 10 則引言,原生渲染 |
其中幾項值得再多說一句。
手寫的分頁程式成功找回全部 12 個預期型錄項目。文章選擇器抓到了標題與預期中的三段內文;周邊的樣板內容仍留在 DOM 裡。Puppeteer 提供的是已渲染的 DOM,而真正決定什麼算文章內容的,是選擇器邏輯,而不是 Puppeteer 本身。
在測試的 HTTP 500 路由上,goto 回傳了可檢視的 500 狀態回應物件,而且沒有丟例外。這只能說明這一種情境;對於 timeout、DNS 失敗、瀏覽器崩潰、frame 被解除附掛,以及其他導航錯誤,仍然得另外處理。
爬取圖那項最能說明全部故事。沿著測試頁的內部連結走到 12 個頁面,並追蹤深度避免重複拜訪 URL,我用的是手寫的 breadth-first search——因為 Puppeteer 沒有內建爬取佇列。結果成功找到全部 12 個頁面,深度分布為 {0:1, 1:4, 2:7},也就是說我的 BFS 是有效的。但 BFS 是我自己寫的。Puppeteer 只負責渲染每個頁面;真正「走網站」的邏輯,是我寫的程式。對十二個頁面來說,這只是十幾行程式,不算什麼。但如果是數千個 URL,還要去重、重試、禮貌延遲,那十幾行就會變成一個專案。
這裡還要再提醒一次一個很容易被誤用的 caveat:這些 artefact 內含各項測試的執行時間,但那只是單次、單機的觀察,不是 benchmark。我不會因為一台筆電、一次執行,就拿來比較 Puppeteer 與其他工具的速度。這些數字能支撐的是:八種不同頁型之間的召回率與行為表現,而不是秒錶級的性能主張。
我沒有測什麼
為了避免結果被解讀得比實際更廣,下面列出本次測試之外、因此也不應算進這些數字的項目:
| 不在本次測試範圍內 | 狀態 |
|---|---|
| 透過 WebDriver BiDi 的 Firefox | 已文件化、24.16.0 可用,但本次未實測 |
| Proxy 與請求攔截 | 未測;這些都是支援功能,但我沒有實跑 |
| 多頁並行規模 | 我只跑小規模;在真實高併發下的瀏覽器群行為未量測 |
| 以最新版本重跑 | 我測的是 24.16.0;截至 2026-07-09,npm 最新版是 25.3.0,已領先一個完整 major version。我實際用到的 API(launch、goto、$$eval、screenshot、頁內 fetch)在 24→25 之間是穩定的,但若要押注精確數字,仍應先在 25.3.0 上重跑 |
這些都不是扣分項,而是一輪 fixture 測試能誠實主張的邊界。
優缺點
優點:
- 可原生渲染 JavaScript,並且有明確等待內容的做法:動態測試頁 8/8、公開示範頁 10 則引言,全都附帶截圖。
- 手寫的分頁與文章選擇器成功抓回預期的測試資料。
- 頁內
fetch可直接從已知的同源端點取回全部 8 筆資料,不需要解析 DOM。 - 實測的 HTTP 500 回應可檢視,且不會拋出例外。
- 預設安裝會下載相容的 Chrome for Testing;同時也能改用其他可執行檔,或跳過下載。
- 以 CDP 為核心、成熟且偏向 Chrome 的 API,生態深、文件完整,且採 Apache-2.0 授權。
- 比外界想像更廣:自 v23 起已文件化支援透過 WebDriver BiDi 的 Firefox。
缺點:
- 沒有內建爬取佇列、資料集寫入器或限速機制——要做爬取規模,得自己寫或加一層包裝。
- 不支援 WebKit,而且跨引擎故事也比 Playwright 年輕。
- 真實瀏覽器重量不小:要下載內建 Chrome,每個頁面也都有記憶體成本,和純 HTTP 工具相比更重。
- 以 Node 為主;若要從其他語言使用,就得自己搭橋並長期維護。
- 我實測的版本(24.16.0)落後 npm 最新版(25.3.0)一個 major version——在信任精確數字前,務必先用最新版本重驗。
適合誰,不適合誰
如果你主要在 Node 生態中工作,目標網站在 Chrome 中能正常渲染(多數情況下都是如此),而你想要的是一個成熟、聚焦的函式庫,把「JavaScript 跑完之後的頁面」變成可以讀、可以截圖的內容,那就該考慮 Puppeteer。若你要抓的是一批動態頁、或者想透過頁面本身的 session 去讀 JSON API、又或者需要把渲染後截圖當成證據,它都是很強、且不太惹事的預設選項。若你之後真的需要 Firefox,BiDi 路徑也已經在那裡;而且它的生態夠深,通常遇到的問題早就有人遇過。
如果你的問題其實是「流程編排」而不是「渲染」,就要三思。若你需要在數百或數千個 URL 間做爬取,還要去重、重試、限速,那只靠 Puppeteer 會逼你手刻一個爬蟲——這對它來說層級不對。如果你的頁面根本不需要 JavaScript 就能拿到資料,那就乾脆不要用無頭瀏覽器;HTTP 請求加 parser 就能完成的工作,硬上真實瀏覽器只是在浪費記憶體與設定時間。如果你需要的是 WebKit 相容性,或非 JavaScript 語言的客戶端,這也不是那把對的工具。
替代方案,以及 Thunderbit 的位置
先講最誠實的定位:Puppeteer 是免費、Apache-2.0、可自行部署的,而你要自己承擔一切——瀏覽器集群、額外包上的爬蟲程式、以及持續不斷的反機器人攻防。對很多專案來說,這種 ownership 正是最正確的選擇;而且沒有任何託管服務能比你自己已有的瀏覽器更便宜地渲染一個已授權的頁面。
在開源方案裡,真正有意義的比較應該是看工作內容,而不是看 logo。若你重視的是爬取規模,Crawlee 就是最自然的搭檔:它的 PuppeteerCrawler 會把 Puppeteer 包上 request queue、dataset 和限速機制,而這些正是 Puppeteer 刻意不提供的,因此你保留了渲染能力,也補上了流程編排。如果你的輸出目標是給 LLM pipeline 用的乾淨 Markdown,而不是渲染 DOM,Crawl4AI 會驅動真實瀏覽器並直接產出那種格式。如果你的頁面根本不需要瀏覽器,那像 Scrapy 這種 HTTP-first 框架,就是不同且更輕量的類別。若你正在同時評估這些選項,我們的 開源爬蟲總整理 會把各類方案並排整理。
像 Thunderbit 這類託管服務,則是把瀏覽器操作與資料擷取包成 API。它沒有跑進這次 Puppeteer 測試流程,因此本篇不會對渲染、封鎖率、擷取品質或成本做一對一比較。真正的決策邊界在於營運責任:你要自己維護瀏覽器與爬蟲程式,還是把那一層交給服務商代管。
使用 Puppeteer 時,你不需要付廠商使用費,但計算資源、頻寬、瀏覽器維護、流程編排與營運責任都還是你的。走託管方案則會按用量收費,並把其中一部分責任轉移給供應商。這次實驗並沒有比較兩者的實際結果。
結論
如果你在 Node 環境中工作,且需要以 Chrome 為核心的瀏覽器自動化,那 Puppeteer 24.16.0 很值得評估。這次手寫的測試程式抓到了 12 個靜態項目、8 個動態項目與 10 則公開示範引言;已知的同源 API 透過 page.evaluate 取回了 8 筆資料;截圖正常運作;而測試中的 HTTP 500 也保持可檢視。這些結果都屬於明確命名的測試頁與較舊的 major version,不代表一般性的擷取召回率。
不過,請把它的能力範圍估得剛剛好。Puppeteer 是渲染器,不是爬蟲:我的 12 頁走訪需要手寫 BFS,因為它沒有內建 queue,而這個缺口在規模放大時就是真工作——要嘛交給 Crawlee,要嘛自己寫整套機制。它是以 Chrome 為先、雖有文件化的 Firefox BiDi 支援,但沒有 WebKit,所以不是跨引擎廣度的工具。它也帶著真實瀏覽器的重量。而且我測的是 24.16.0,對照的是 25.3.0 最新版,所以在相信精確數字之前,記得先在當前版本重跑。只要你先知道這四件事,Puppeteer 就是一個很出色的 Chrome 自動化函式庫;若你期待它自動幫你爬完整個網站,那你最後會寫出本來以為自己已經下載好的那個爬蟲。
試試 Thunderbit 進行網頁資料擷取 Get Started Free
常見問題
Puppeteer 會原生渲染 JavaScript 頁面嗎,還是需要外掛?
它會原生渲染,不需要外掛。在我的動態測試頁上,它以 1.0 的召回率抓到 8/8 個前端生成的商品,並成功輸出整頁截圖;公開的 Quotes to Scrape JS 頁也同樣抓到全部 10 則引言——流程就是先 goto,再讀取已渲染的 DOM。因為 Puppeteer 是透過 DevTools Protocol 操控真實 Chrome,所以頁面腳本會在你讀取之前先跑完。
Puppeteer 能不解析 HTML 就直接抓 JSON API 嗎?
可以,只要端點與請求契約允許。page.evaluate 可以從頁面 origin 發出請求,也可能重用符合條件的 cookie;但它不會自動重現應用程式自己的標頭、token、選項或 service worker 行為。在同源測試頁上,它不經 DOM 解析就取回了全部 8 筆資料。
Puppeteer 算是網頁爬蟲嗎? 不算——它是瀏覽器自動化函式庫,不是爬蟲框架。它沒有內建 request queue、dataset writer 或限速,所以我的 12 頁爬取(深度 {0:1, 1:4, 2:7})需要手寫 breadth-first search。這是範圍邊界,不是缺陷。若要做爬取規模,最好搭配像 Crawlee 的 PuppeteerCrawler 這種包裝器,補上 Puppeteer 原本不提供的 queue 和 dataset 機制。
Puppeteer 只能跑 Chrome 嗎? 不再是了。它是以 CDP 為核心、Chrome 優先,但自 v23 起已文件化支援透過 WebDriver BiDi 的 Firefox,而我測試的版本 24.16.0 早就超過那個版本。它不支援的是 WebKit,而且跨引擎故事比 Playwright 年輕——真正的限制是這個,而不是「只能跑 Chrome」。我這裡只實測了 Chrome,所以對 Firefox/BiDi 的描述是依文件所載,而不是我實際量到的結果。
安裝 Puppeteer 時實際會下載什麼?
預設情況下,npm install puppeteer 會下載相容的 Chrome for Testing 版本。這個下載可以被跳過或改路徑,也可以配置其他可執行檔,因此版本對齊取決於你的部署方式。請為瀏覽器預留磁碟與頻寬,尤其是在沒有快取的 CI runner 上。本篇測試的是 24.16.0;若要發佈時使用,請用當前版本重新跑一次核心測試頁。


