Browser Automation 技術比較與偽裝優化

瀏覽器自動化框架、反偵測與效能分享

By XD3an

Browser Automation(瀏覽器自動化) 是指利用程式控制瀏覽器自動執行各種操作,如自動化測試、資料爬取、表單填寫、搶票、批次操作等。現代自動化框架不僅能模擬人類點擊、輸入、滑動等行為,還能處理複雜的 JavaScript 動態網頁、驗證碼、反爬蟲機制。

選擇合適的自動化技術時,需考慮 支援語言瀏覽器兼容性反偵測能力效能生態系統 等因素。下表整理了主流框架的特色與適用場景,協助你快速選型。

🧩 Browser Automation 框架比較


技術方案 支援語言 瀏覽器支援 核心特色 最佳應用場景
Playwright Python, JS/TS, C#, Java Chromium, Firefox, WebKit WebSocket 通信、自動等待、網路攔截 現代化測試與爬蟲、CI/CD 整合
Selenium Python, Java, C#, 多語言 Chrome, Firefox, Safari, Edge, IE WebDriver + HTTP、Selenium Grid、生態成熟 跨瀏覽器企業級自動化、兼容舊瀏覽器
NoDriver Python (async) Chromium 無 WebDriver、異步 CDP 通信、高效率 高效反偵測爬蟲、並行多頁面抓取
Pyppeteer Python wrapper Chromium 輕量、DevTools Protocol、非同步支援 JS-heavy 網站抓取、低開銷自動化
undetected_chromedriver Python Chromium-based WebDriver 補丁、提升避檢測能力 面對 Cloudflare、Akamai 等防禦機制
zendriver Python Chromium nodriver fork、功能一致 更積極維護的 undetected/nodriver 替代品
🔗 參考連結

🦾 Camouflage & Performance Optimization(偽裝與效能優化)


  • navigator.webdriver 隱藏
    • 原理:自動化瀏覽器會自動帶有 navigator.webdriver = true,網站可用此判斷是否為機器人。將其設為 undefined 可繞過大多數自動化偵測。
    • 原因:這是最常見的自動化檢查點,必須優先隱藏。
from playwright.sync_api import sync_playwright

with sync_playwright() as playwright:
    browser = playwright.chromium.launch(headless=False)
    page = browser.new_page()
    # 注入 JS 隱藏 navigator.webdriver
    page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
    page.goto("https://tixcraft.com/")
    input("按 Enter 關閉瀏覽器...")
    browser.close()
  • navigator.plugins、navigator.languages 偽裝
    • 原理:真實瀏覽器會有多個 plugins(如 PDF、Flash)及多語言,機器人常為空陣列或異常。偽造常見值可混淆檢查。
    • 原因:部分網站會檢查 plugins/languages 來判斷是否為正常用戶。
page.add_init_script(`
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3]});
Object.defineProperty(navigator, 'languages', {get: () => ['zh-TW', 'zh', 'en']});
`)
  • window.chrome 物件偽裝
    • 原理:Chrome 會有 window.chrome 物件,機器人常缺少。補上可避免被判斷為自動化。
    • 原因:部分網站用此判斷是否為真實 Chrome。
page.add_init_script(`
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3]});
Object.defineProperty(navigator, 'languages', {get: () => ['zh-TW', 'zh', 'en']});
window.chrome = window.chrome || {};
window.chrome.runtime = {};
`)
  • Permissions API 偽裝
    • 原理:自動化瀏覽器的權限查詢行為與真實用戶不同,覆寫 query 方法可避免異常回傳。
    • 原因:網站可用權限查詢結果判斷自動化。
page.add_init_script(`
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
);
`)
  • Canvas/WebGL/Audio 指紋偽裝
    • 原理:網站可用畫布、WebGL、音訊 API 產生指紋,機器人常有固定特徵。覆寫相關方法可降低指紋辨識率。
    • 原因:進階防爬網站會用這些 API 產生唯一指紋。
  • HTTP headers 偽裝
    • 原理:自動化 context 常缺少 sec-ch-ua、accept-language 等 headers,或內容異常。自動推導並補齊可模擬真實瀏覽器請求。
    • 原因:網站可用 headers 組合判斷是否為自動化。
context = browser.new_context(
    user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    locale="zh-TW",
    extra_http_headers={
        "accept-language": "zh-TW,zh;q=0.9,en;q=0.8",
        "sec-ch-ua": '"Chromium";v="120", "Not:A-Brand";v="99"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',
    }
)
  • hardwareConcurrency/deviceMemory 偽裝
    • 原理:真實裝置多為 4/8/16 核心、4/8GB 記憶體,機器人常為預設值。偽造常見值可混淆檢查。
    • 原因:部分網站會檢查這些屬性判斷是否為虛擬機或自動化。
page.add_init_script(`
Object.defineProperty(navigator, 'hardwareConcurrency', {get: () => 8});
Object.defineProperty(navigator, 'deviceMemory', {get: () => 8});
`)
  • Media Codecs、chrome.csi/loadTimes 偽裝
    • 原理:真實 Chrome 支援多種影音格式,且有特殊 API。補上這些屬性可避免被判斷為自動化。
    • 原因:進階網站會檢查這些 API 是否存在。
page.add_init_script(`// 偽裝 Media Codecs
const origCanPlayType = HTMLMediaElement.prototype.canPlayType;
HTMLMediaElement.prototype.canPlayType = function(type) {
    if(type === 'video/mp4' || type === 'audio/mp3') return 'probably';
    return origCanPlayType.call(this, type);
};
// 偽裝 chrome.csi/loadTimes
window.chrome = window.chrome || {};
window.chrome.csi = () => ({ startE: Date.now(), onloadT: Date.now(), pageT: Date.now(), tran: 15 });
window.chrome.loadTimes = () => ({
    requestTime: Date.now()/1000,
    startLoadTime: Date.now()/1000,
    commitLoadTime: Date.now()/1000,
    finishDocumentLoadTime: Date.now()/1000,
    finishLoadTime: Date.now()/1000,
    firstPaintTime: Date.now()/1000,
    navigationType: 'Other',
    wasFetchedViaSpdy: false,
    wasNpnNegotiated: false,
    npnNegotiatedProtocol: '',
    wasAlternateProtocolAvailable: false,
    connectionInfo: 'h2'
});
`)
  • Storage(localStorage/sessionStorage)隨機化
    • 原理:自動化 context 常為乾淨狀態,真實用戶多有歷史資料。隨機填充可降低指紋重複。
    • 原因:網站可用 storage 內容判斷是否為新開的自動化分頁。
page.add_init_script(`
try {
    localStorage.setItem('bot_rand', Math.random().toString(36).substring(2));
    sessionStorage.setItem('bot_rand', Math.random().toString(36).substring(2));
} catch(e) {}
`)
  • 滑鼠/鍵盤行為偽裝
    • 原理:自動化操作常無滑鼠/鍵盤事件,或事件間隔異常。隨機觸發 mousemove、keydown、focus/blur 等事件可模擬人類行為。
    • 原因:網站可用行為軌跡判斷是否為機器人。
page.add_init_script(`
function randomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
setTimeout(() => {
    // 多次隨機 mousemove
    for (let i = 0; i < randomInt(2, 5); i++) {
        const mouseEvent = new MouseEvent('mousemove', {
            clientX: randomInt(0, window.innerWidth),
            clientY: randomInt(0, window.innerHeight),
            bubbles: true
        });
        document.dispatchEvent(mouseEvent);
    }
    // 多次隨機 keydown
    const keys = ['a', 's', 'd', 'f', 'j', 'k', 'l'];
    for (let i = 0; i < randomInt(1, 3); i++) {
        const key = keys[randomInt(0, keys.length - 1)];
        const keyEvent = new KeyboardEvent('keydown', { key: key, bubbles: true });
        document.dispatchEvent(keyEvent);
    }
}, 500 + Math.random() * 1000);
`)
  • Captcha/滑塊驗證偽裝
    • 原理:部分網站會檢查是否有自動化腳本處理驗證。預留偽裝標記可降低被特殊驗證腳本識破。
    • 原因:防止網站用 JS 偵測自動化解驗證。
page.add_init_script(`
window.__bot_captcha_placeholder = true;
Object.defineProperty(window, 'isBot', { get: () => false });
Object.defineProperty(window, 'isRobot', { get: () => false });
`)