如何阻擋網頁彈出 alert 對話框

文章分享 4種解法:

  • chrome extension 擴充元件
  • UC / selenium
  • nodriver / cdp.page
  • Chrome flag

前3種可以只擋 alert(), 第4種可以快速度擋掉三種對話框 (alert/prompt/confirm).


chrome extension 擴充元件

使用 chrome extension 只需寫一個 content.js, 設定為 “run_at”: “document_start”, 然後覆寫掉 window.alert() 就可以擋掉 alert() 彈出視窗。

{
  "manifest_version": 3,
  "name": "My Page Start Extension",
  "version": "1.0",
  "content_scripts": [
    {
      "matches": ["<all_urls>"], 
      "js": ["content.js"],
      "run_at": "document_start" 
    }
  ]
}

UC / selenium

範例程式:

    try:
        url = driver.current_url
    except NoSuchWindowException:
        print('NoSuchWindowException at this url:', url )
        pass
    except UnexpectedAlertPresentException as exc1:
        print('UnexpectedAlertPresentException at this url:', url )
        try:
            driver.switch_to.alert.accept()
        except Exception as exc:
            pass
    except Exception as exc:
        logger.error(exc, exc_info=True)

nodriver / cdp.page

範例程式:

if not driver is None:
    tab = driver.main_tab
    await tab.send(cdp.page.enable())
    await tab.send(cdp.page.add_script_to_evaluate_on_new_document(source="""
// 覆寫 alert() 為空函數
(function() {
    const fakeAlert = function() {};
    const nativeFuncToString = Function.prototype.toString;
    Object.defineProperty(window, "alert", {
        value: fakeAlert,
        writable: false,
        configurable: false
    });
    fakeAlert.toString = function() {
        return "function alert() { [native code] }";
    };
    Object.defineProperty(Function.prototype, "toString", {
        value: function() {
            if (this === fakeAlert) {
                return "function alert() { [native code] }";
            }
            return nativeFuncToString.call(this);
        }
    });
})();

Object.defineProperty(window.alert, "name", { value: "alert" });

document.addEventListener("DOMContentLoaded", () => {
  for (const frame of document.querySelectorAll("iframe")) {
    try {
      frame.contentWindow.alert = window.alert;
    } catch(e){}
  }
});
    """))

nodriver 三種不同解法詳述:


nodriver 解法 1:直接「輪詢 CDP 事件」攔截 alert

在 nodriver 裡,讓底層 CDP 事件仍然會送回來,你可以:

  • 開一個 async task 一直讀事件
  • 如果遇到 Page.javascriptDialogOpening
  • 就立刻呼叫 Page.handleJavaScriptDialog 把彈窗關掉

範例:

import asyncio
from nodriver import cdp

async def auto_close_dialogs(tab):
    while True:
        msg = await tab.recv()    # 持續接收 CDP 訊息
        if msg and msg.get("method") == "Page.javascriptDialogOpening":
            print("拦截到 dialog:", msg["params"])
            await tab.send(
                cdp.page.handleJavaScriptDialog(accept=False)
            )

# 啟動
tab = driver.main_tab

# 需要先 enable Page domain
await tab.send(cdp.page.enable())

# 丟到 background task
asyncio.create_task(auto_close_dialogs(tab))

這種方式,幾乎所有 CDP 客戶端都能用。


nodriver 解法 2:更強力的 JS 覆寫(讓網站幾乎無法復原)

有些網站會:

  • 先備份 alert
  • 或用 sandbox iframe
  • 或用 window.__proto__.alert

所以你需要把 alert 從 prototype 層強制鎖死:

await tab.send(
    cdp.page.add_script_to_evaluate_on_new_document(
        source="""
        (function(){
            const emptyFn = function(){};

            // 覆寫自身
            window.alert = emptyFn;
            window.confirm = () => true;
            window.prompt = () => null;

            // 從原型鏈封住
            Object.defineProperty(Window.prototype, "alert", {
                value: emptyFn,
                configurable: false,
                writable: false
            });
            Object.defineProperty(Window.prototype, "confirm", {
                value: () => true,
                configurable: false,
                writable: false
            });
            Object.defineProperty(Window.prototype, "prompt", {
                value: () => null,
                configurable: false,
                writable: false
            });
        })();
        """
    )
)

這招比一般覆寫強很多,連 sandbox iframe 通常也無法再改回去


uc / nodriver 解法 3:啟動 Chrome 時直接禁止 JS 彈窗(最暴力)

Chrome/Chromium 有旗標可以完全禁用 JS dialogs:

--disable-javascript-dialogs

如果你是:

  • 自己啟動 Chrome
  • 或 nodriver 有 launch options

那可以直接:

driver = await nodriver.start(
    chrome_args=[
        "--disable-javascript-dialogs"
    ]
)

啟動後:

  • alert / confirm / prompt 完全失效
  • 連 CDP 都不會再收到 dialog 事件

這是 最高效也最徹底 的解法。


適用時間點

方法特性適用
解法 1:接收 CDP 事件按下去彈窗瞬間就自動關掉無法修改 Chrome 啟動參數或網站很難 hack 時
解法 2:高強度 JS override大部分網站連出彈窗都做不到想靠 JavaScript 方式解決
解法 3:Chrome flag最乾淨、完全禁止彈窗能控制瀏覽器啟動參數時

以下為網頁測試用的 javascript, 固定彈出 alert()

<script>alert('您所輸入的資料不正確');</script>

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *