要約 (TL;DR)
- alert()/confirm()/prompt()/beforeunloadはJavaScriptをブロックする
- Rodではgoroutineを使って処理することが推奨されている
wait, handle := page.MustHandleDialog()
go page.MustElement("button").MustClick()
wait()
handle(true, "")
rod/page.go at v0.116.2 #L384C1-L391 · go-rod/rod · GitHub
環境
- go-rod v0.116.2
GitHub - go-rod/rod: A Chrome DevTools Protocol driver for web automation and scraping.
What is go-rod?
A Chrome DevTools Protocol driver for web automation and scraping.
go-rod
は、CDP(Chrome DevTools Protocol)
を使ってブラウザ操作の自動化やスクレイピングを行うためのツールです。
事象
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Alert Sample</title>
</head>
<body>
<input type="text" id="input" />
<button id="btn_valid" onclick="document.getElementById('input').value.length > 2 ? alert('2文字以内で入力してください') : null;">Click me</button>
</body>
</html>
page := browser.MustPage("./sample.html")
page.MustElement("#input").MustInput("123")
page.MustElement("#btn_valid").MustClick()
// 後続処理...
上記は、入力フォームをJavaScriptでチェックする場合に、エラーメッセージをalert()で出力しているサンプルです。
このスクリプトを動かすと、page.MustElement("#btn_valid").MustClick()
で処理が止まってしまい後続処理がまったく動かない状態になります。
原因
alert()が実行されると[OK]が押されるまで、JavaScriptがブロックされる挙動になってしまうためでした。
この挙動について調べると、「ブラウザ自動操作ライブラリ全般に共通する課題」のようで、SeleniumではUnexpectedAlertPresentException
という例外がスローされて処理が止まるようです。
【Python Selenium】UnexpectedAlertPresentException:Chromeポップアップ操作 | OFFICE54
対処法
// 1) ダイアログのリスナーとハンドラ取得
wait, handle := page.MustHandleDialog()
// 2) クリックなどダイアログを“発火させる操作”は別goroutineで走らせる
go page.MustElement("#btn_valid").MustClick()
// 3) 実際にダイアログが開くのを待つ(1回限り)
wait()
// 4) 受理/却下やprompt入力を行う
handle(true, "") // OKを押す(confirmならOK、promptなら第2引数に文字列)
// 後続処理...
上記は事象サンプルのコードに、go-rodライブラリでalertダイアログを処理する推奨コード例を参考に変更を加えたものです。
wait, handle := page.MustHandleDialog()
go page.MustElement("button").MustClick()
wait()
handle(true, "")
rod/page.go at v0.116.2 #L384C1-L391 · go-rod/rod · GitHub
page.MustElement("button").MustClick()
をgoroutineを使い、別スレッドで動かす。
alertダイアログが表示されるとJSブロックするので、別スレッドで動かしています。
wait, handle := page.MustHandleDialog()
は、Page.javascriptDialogOpening
イベントを一回に限り待つリスナーのwaitとイベント時に処理する関数のhandleを返します。
Page.javascriptDialogOpening
イベントは、alert()などのJSダイアログが表示される時に呼ばれます。
wait()
で、JSダイアログが表示されるのを一回に限り待つ。
handle(true, "")
で、JSダイアログでOKボタンを押下する。
- type PageJavascriptDialogOpening - github.com/go-rod/rod/lib/proto - Go Packages
- Page.javascriptDialogOpening - Chrome DevTools Protocol
入力値によってJSダイアログの表示有無が変わる場合
wait, handle := page.MustHandleDialog()
を行います。
wait()
でタイムアウトした場合は、JSダイアログが表示されなかったとして処理すればOKです。
Goでのパッケージ公開練習と、備忘録を兼ねて上記処理をラップした関数をgo-rodのユーティリティ関数として定義していますので、参考までに
https://github.com/buzzword111/rod-util