文章分享 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>
