Python Playwright 爬蟲完整教學:安裝、定位器、等待機制與反偵測實戰(2026)

Playwright 是微軟開源的瀏覽器自動化工具,能輕鬆爬取 JavaScript 動態渲染的網站。這篇 Python Playwright 爬蟲教學從安裝、Browser/Context/Page 架構、定位器選擇、自動等待到反爬蟲對策一次講完,附可直接執行的範例程式碼(已實測驗證)。

  • Dennis
  • 7 分鐘閱讀
Python Playwright 爬蟲完整教學:安裝、定位器、等待機制與反偵測實戰(2026)

Playwright 是微軟開發的開源瀏覽器自動化框架,用 Python 寫爬蟲時,它能完整模擬真實瀏覽器(Chromium、Firefox、WebKit),輕鬆取得 JavaScript 動態渲染的內容,並內建自動等待機制大幅降低「元素還沒載入就失敗」的困擾。本文從安裝到實戰一步步帶你寫出第一個 Playwright 爬蟲。

為什麼爬蟲要選 Playwright?

傳統爬蟲用 requests 抓 HTML,但遇到以下情況就會卡關:

  • 網頁內容由 JavaScript 動態渲染(SPA、Vue、React 網站)
  • 需要點擊、滾動、填表單才能觸發內容載入
  • 網站會偵測「非瀏覽器」的請求並擋掉

Playwright 直接開一個真的瀏覽器去跑,這些問題迎刃而解。跟 Selenium 相比,它最大的優勢是不用另外下載驅動程式(不會遇到瀏覽器一更新驅動就失效的問題),而且自動等待機制讓腳本穩定很多。

比較項目requestsSeleniumPlaywright
JS 動態渲染❌ 拿不到
自動等待元素❌ 手動 sleep⚠️ WebDriverWait✅ 內建
驅動程式管理不需要❌ 要自己下載✅ 自動
反偵測難度中高(可搭配 stealth)
跨瀏覽器不適用✅ Chromium/Firefox/WebKit

安裝與初始化

pip install playwright
playwright install chromium   # 下載 Chromium 瀏覽器(爬蟲最常用)

💡 不需要像 Selenium 那樣手動下載 chromedriver,playwright install 一次搞定,瀏覽器更新也不用擔心驅動失效。

核心架構:Browser、Context、Page

Playwright 是三層架構,理解它才能寫出乾淨的爬蟲:

graph TD
    B[Browser 瀏覽器實例] --> C1[Context 1 登入態環境]
    B --> C2[Context 2 匿名環境]
    C1 --> P1[Page 頁面操作<br/>DOM / Network]
    C2 --> P2[Page 頁面操作<br/>DOM / Network]
  • Browser:一個獨立的瀏覽器進程
  • Browser Context:獨立的沙盒環境,各有自己的 Cookie、localStorage —— 可以同時處理登入態與匿名態,互不干擾
  • Page:實際執行網頁操作、讀取 DOM 的標籤頁

第一個 Playwright 爬蟲

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    # 啟動 Chromium(headless=False 可開視窗觀察)
    browser = p.chromium.launch(headless=True)
    context = browser.new_context(
        viewport={"width": 1280, "height": 800},  # 模擬桌面視窗
        locale="zh-TW",
    )
    page = context.new_page()
    page.goto("https://example.com")
    print(page.title())       # 取得頁面標題
    browser.close()

Python 版同時支援同步非同步(async)API,一般爬蟲用同步就夠了。

定位器(Locator):選元素的正確姿勢

Playwright 的定位器有「延遲求值」特性——定義時不會立刻查詢 DOM,而是執行操作時才解析,因此對動態內容特別友善。官方建議依循這個決策樹挑選定位方式:

  1. get_by_role() — 按 ARIA 角色與名稱(按鈕、連結)
  2. get_by_label() — 表單欄位對應的 Label 文字
  3. get_by_placeholder() — 輸入框的提示文字
  4. get_by_text() — 畫面上的可見文字
  5. get_by_test_id() — 有 data-testid 屬性時
  6. CSS / XPath — 最後手段,互動元素盡量避免
page.get_by_role("button", name="提交").click()
page.get_by_placeholder("請輸入帳號").fill("my_user")
page.get_by_text("登入成功")           # 文字定位
page.locator("div.card > a.link")     # CSS(不得已才用)

進階技巧:鏈式定位與過濾,適合處理重複結構(卡片、表格列):

# 鏈式定位:先鎖定卡片,再找裡面的按鈕
page.locator("div.card").get_by_role("button", name="詳情")

# 過濾:只找含特定文字的列
page.locator("tr").filter(has_text="待付款")

# 取第 N 個 / 第一個 / 最後一個
page.locator("li.item").first
page.locator("li.item").nth(2)

⚠️ Playwright 預設嚴格模式:如果定位器同時命中多個元素,操作時會直接報「Strict Mode Violation」。這是好事——它提醒你定位器寫得不夠精準,用鏈式、filter 或 .first 解決。

等待機制:不要再寫 time.sleep()

Playwright 最大的賣點就是自動等待:執行 click()fill() 等動作前,它會自動確認元素「已附加、可見、穩定(無動畫)、已啟用、沒被覆蓋」,全部通過才動手。所以絕大多數情況你不需要自己加等待

需要等待特定條件時,用這些:

# 等某個元素出現或消失
page.wait_for_selector("div.result-item", state="visible")

# 等 URL 改變(適合表單提交後)
page.wait_for_url("**/dashboard")

# Web-first 斷言(會自動重試直到條件滿足)
from playwright.sync_api import expect
expect(page.get_by_text("儲存成功")).to_be_visible()

三個常見陷阱

錯誤寫法問題正確做法
time.sleep(5)阻塞 Playwright 內部事件迴圈,狀態不同步page.wait_for_timeout(ms)(僅除錯用)
wait_until="networkidle"現代網站有 analytics/WebSocket 背景請求,永遠等不到明確等待目標 selector 出現
固定等待 5 秒「確保載入」又慢又不可靠等「明確的應用程式訊號」(元素/URL/API 回應)

動態載入與無限滾動實戰

很多網站用「滾動觸發懶載入」或「按按鈕載入更多」。對策有三種,優先順序從高到低

1. 攔截 API 回應(最穩定高效)

很多動態網站的資料來自後端 JSON API,直接攔截比解析 DOM 更快更穩:

with page.expect_response(lambda r: "/api/get_data" in r.url and r.status == 200) as resp:
    page.get_by_role("button", name="載入更多").click()
json_data = resp.value.json()   # 直接拿到結構化資料

2. 模擬滾動觸發載入

for _ in range(5):
    page.mouse.wheel(0, 1500)   # 向下滾 1500 像素
    page.wait_for_timeout(800)  # 給渲染一點時間(除錯用途可接受)

3. 等待目標元素出現

page.locator(".quote").first.wait_for(state="visible", timeout=15000)

完整範例:爬取 JS 渲染網站(已實測可跑)

quotes.toscrape.com/js 是官方提供的練習站,名言內容透過 JavaScript 動態渲染,非常適合驗證 Playwright 功力。以下範例我實測過,直接複製就能跑:

"""Playwright 爬蟲教學範例 — quotes.toscrape.com (JS 動態渲染版)"""
import csv
from playwright.sync_api import sync_playwright

BASE_URL = "https://quotes.toscrape.com/js/"

def main():
    results = []
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context(
            viewport={"width": 1280, "height": 800},
            locale="zh-TW",
        )
        page = context.new_page()
        page.goto(BASE_URL)
        # 自動等待:等第一張卡片出現(Playwright 會自動重試直到可見)
        page.locator(".quote").first.wait_for(state="visible", timeout=15000)

        # 抓取第一頁所有名言
        quotes = page.locator(".quote").all()
        for q in quotes:
            text = q.locator(".text").text_content().strip()
            author = q.locator(".author").text_content().strip()
            results.append({"text": text, "author": author})
            print(f"「{text[:30]}...」 — {author}")

        browser.close()

    # 輸出 CSV(標準庫即可,不必裝 pandas)
    with open("quotes.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["text", "author"])
        writer.writeheader()
        writer.writerows(results)
    print(f"共儲存 {len(results)} 筆到 quotes.csv")

if __name__ == "__main__":
    main()

執行結果(實測輸出):

「The world as we have created it is a pr...」 — Albert Einstein
「It is our choices, Harry, that show wha...」 — J.K. Rowling
「There are only two ways to live your li...」 — Albert Einstein
共儲存 10 筆到 quotes.csv

反偵測實戰:被 Cloudflare 擋怎麼辦?

愈來愈多網站(包括不少台灣電商、租屋平台)使用 Cloudflare 等防護。Playwright 預設會暴露一些特徵(例如 navigator.webdriver = true),會被快速識別為機器人。基本對策:

1. 偽裝環境參數

context = browser.new_context(
    viewport={"width": 1920, "height": 1080},
    user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    locale="zh-TW",
    timezone_id="Asia/Taipei",
    extra_http_headers={"Accept-Language": "zh-TW,zh;q=0.9,en;q=0.8"},
)

2. 使用 playwright-stealth 隱藏自動化特徵

pip install playwright-stealth
from playwright_stealth import stealth_sync

page = context.new_page()
stealth_sync(page)   # 把 navigator.webdriver 等特徵偽裝成真實瀏覽器

3. 其他實用技巧

  • 持久化 Context:用 launch_persistent_context(user_data_dir="./profile") 保留 Cookie 與登入態,避免每次都是「全新機器人」
  • Proxy 輪換:需要大量抓取時,在 launch(proxy={...}) 設定代理
  • 模擬真人行為press_sequentially("關鍵字", delay=100) 逐字輸入、隨機滾動間隔

⚠️ 誠實提醒:沒有「銀彈」能保證通過所有反爬蟲系統,Cloudflare Turnstile 這類 CAPTCHA 還是需要真人或付費解決服務。爬蟲務必遵守 robots.txt 與網站使用條款,控制請求頻率,做個有禮貌的爬蟲。

總結與下一步

Playwright 是目前 Python 爬蟲的首選工具,重點記住四件事:用語義化定位器(get_by_role 優先)、相信自動等待(少寫 sleep)、優先攔截 API 而非解析 DOM被擋時先偽裝環境再談 stealth

如果覺得手寫爬蟲太累,也可以看看 AI 時代的新玩法:用 LLM 直接從網頁提取結構化資料(LLM Scraper),或讓 AI 瀏覽代理幫你完成整個流程(browser-use)。但理解 Playwright 的底層原理,永遠是進階的基礎。

延伸閱讀

參考來源

📬 訂閱 most.tw 電子報

每週精選 AI 工具教學與技術乾貨,直接送到你的信箱。免費、隨時可退訂。