WEBVIEW2を使用してみる(4)HTML要素の検索と操作
前回に引き続きDOM操作のテスト。
やってみたいこと
・ WebView2へ表示したサイトより指定の要素を検索する。
・ 検索した要素(テキストボックス)へ検索ワードをセット
・ 検索した要素(検索ボタン)より検索を実行
・ 今回はGoogleさんの検索ボックスでテストさせて頂こうとおもいます。
開発環境
・ Windows10
・ Microsoft Visual Studio 2019 (VB .NET)
WebBrowserのようにHtmlElement操作できないのか?
WebBrowserだと、WebBrowser.DocumentでHtmlElementが操作できたがWebView2には
同等の機能はないみたい。事前調べで知ってはいたがjavaScriptで代用するしかないとのこと。
しかし今までの仕組みと互換性なくなるよなぁ。本当にできないの?と、ちょっと疑問だったので
前回JavaScriptとVBを連携させた要領で
・サイト側から”document.body.outerHTML”でHTMLを取得
・HTMLをHtmlDocumentクラスへ変換してgetElementById関数などを使用
という力業ができないかやってみたのですが上手くいかず。。。
時間ももったいないし、まぁ推奨がJavaScriptを使ってってことだろうから
切り替えてJavaScriptで代用するやりかたを先に進める。
要素検索して文字列を入力する。要素検索してクリックする。
例のごとくExecuteScriptAsyncを使ってJavaScriptをセットする。
JavaScript側ではhtmlElmentで要素扱えるので、以下のようにタグ等の要素情報を渡して
内部検索させる。ヒットしたら.valueで文字列セット。クリックの場合なら.click()を実行。
ちなみにJavaScriptをFunction化してるのは連続して同じJavaScriptをExecuteScriptAsync
を実行させたときletなどの変数定義も2回されてしまうみたいで2度目は実行がされなかったため。
しかしデバッグがVisualStudioでなくブラウザ側のデバッグツールでやらないといけないのは、
なかなか慣れない。このあたりも効率化調べなければ
''' <summary>
''' 指定情報(タグ、クラス名、Id、内部テキスト)より要素判定して入力
''' </summary>
''' <param name="tag"></param>
''' <param name="classname"></param>
''' <param name="id"></param>
''' <param name="innertext"></param>
''' <param name="setword"></param>
Public Async Sub ElementInput(ByVal tag As String, ByVal classname As String, ByVal id As String, ByVal innertext As String, ByVal setword As String)
'サイト読み込み完了時以外は実行しない
If Me.NowStatus <> NAVIGATE_STATUS.Complate Then Exit Sub
'指定HTML要素をクリック
Dim js As New System.Text.StringBuilder()
js.AppendLine("function ElementInput(tag, classname, id, innertext, setword)")
js.AppendLine("{")
js.AppendLine(" let items = document.getElementsByTagName(tag);")
js.AppendLine(" for (let i = 0; i < items.length; i++)")
js.AppendLine(" {")
js.AppendLine(" if (items[i].className == classname && items[i].id == id && items[i].innerText == innertext)")
js.AppendLine(" {")
js.AppendLine(" console.log('ElementInput Run');")
js.AppendLine(" items[i].value = setword;")
js.AppendLine(" break;")
js.AppendLine(" }")
js.AppendLine(" }")
js.AppendLine("}")
js.AppendLine("ElementInput('" & tag & "','" & classname & "','" & id & "','" & innertext & "','" & setword & "');")
Await Me.ExecuteScriptAsync(js.ToString())
End Sub
関数はわざわざ2個作ってるけど、内部検索の部分は共通なのでもっとすっきりできそう。
サンプルコード
ClsWebViewへ要素クリック用の関数と要素入力用の関数を追加。
AddJsEvent関数をちょっと修正(onclick→onmouse)
ClsWebView.vb
''' <summary>
''' 指定情報(タグ、クラス名、Id、内部テキスト)より要素判定してクリック
''' </summary>
''' <param name="tag"></param>
''' <param name="classname"></param>
''' <param name="innertext"></param>
Public Async Sub ElementClick(ByVal tag As String, ByVal classname As String, ByVal id As String, ByVal innertext As String)
'サイト読み込み完了時以外は実行しない
If Me.NowStatus <> NAVIGATE_STATUS.Complate Then Exit Sub
'指定HTML要素をクリック
Dim js As New System.Text.StringBuilder()
js.AppendLine("function ElementClick(tag, classname, id, innertext)")
js.AppendLine("{")
js.AppendLine(" let items = document.getElementsByTagName(tag);")
js.AppendLine(" for (let i = 0; i < items.length; i++)")
js.AppendLine(" {")
js.AppendLine(" if (items[i].className == classname && items[i].id == id && items[i].innerText == innertext)")
js.AppendLine(" {")
js.AppendLine(" console.log('ElementClick Run');")
js.AppendLine(" items[i].click();")
js.AppendLine(" break;")
js.AppendLine(" }")
js.AppendLine(" }")
js.AppendLine("}")
js.AppendLine("ElementClick('" & tag & "','" & classname & "','" & id & "','" & innertext & "');")
Await Me.ExecuteScriptAsync(js.ToString())
End Sub
''' <summary>
''' 指定情報(タグ、クラス名、Id、内部テキスト)より要素判定して入力
''' </summary>
''' <param name="tag"></param>
''' <param name="classname"></param>
''' <param name="id"></param>
''' <param name="innertext"></param>
''' <param name="setword"></param>
Public Async Sub ElementInput(ByVal tag As String, ByVal classname As String, ByVal id As String, ByVal innertext As String, ByVal setword As String)
'サイト読み込み完了時以外は実行しない
If Me.NowStatus <> NAVIGATE_STATUS.Complate Then Exit Sub
'指定HTML要素をクリック
Dim js As New System.Text.StringBuilder()
js.AppendLine("function ElementInput(tag, classname, id, innertext, setword)")
js.AppendLine("{")
js.AppendLine(" let items = document.getElementsByTagName(tag);")
js.AppendLine(" for (let i = 0; i < items.length; i++)")
js.AppendLine(" {")
js.AppendLine(" if (items[i].className == classname && items[i].id == id && items[i].innerText == innertext)")
js.AppendLine(" {")
js.AppendLine(" console.log('ElementInput Run');")
js.AppendLine(" items[i].value = setword;")
js.AppendLine(" break;")
js.AppendLine(" }")
js.AppendLine(" }")
js.AppendLine("}")
js.AppendLine("ElementInput('" & tag & "','" & classname & "','" & id & "','" & innertext & "','" & setword & "');")
Await Me.ExecuteScriptAsync(js.ToString())
End Sub
''' <summary>
''' サイト内にJavaScriptイベント追加
''' </summary>
Public Async Sub AddJsEvent()
'マウスクリックイベントを追加
Dim jsClickEvent As New System.Text.StringBuilder()
'onclickで拾えない要素があった + ElementClickでクリック操作するためonclick >> onmousedownに変更
'jsClickEvent.AppendLine("document.body.onclick = function(event) { ")
jsClickEvent.AppendLine("document.body.onmousedown = function(event) { ")
jsClickEvent.AppendLine(" window.chrome.webview.postMessage(" &
"'[TagName] ' + " & "event.target.tagName + " &
"'\n[ClassName] ' + " & "event.target.className + " &
"'\n[id] ' + " & "event.target.id + " &
"'\n[innertext] ' + " & "event.target.innerText + " &
"'\n[outerHTML] ' + " & "event.target.outerHTML" &
"); ")
jsClickEvent.AppendLine(" return false;")
jsClickEvent.AppendLine("}; ")
Await Me.ExecuteScriptAsync(jsClickEvent.ToString())
End Sub
Form1側は、あらたにautoExecBtnコントロールを追加。
クリックすると自動で検索してくれる。検索ワードは日付にちなんで「ひなまつり」。
Form1.vb
''' <summary>
''' フォームロード
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
…(省略)…
'ボタン作成 + コントロール配置
Dim autoExecBtn As Button = New Button()
Me.Controls.Add(autoExecBtn)
autoExecBtn.Text = "自動実行"
autoExecBtn.Left = navigateBtn.Width
autoExecBtn.Top = 0
autoExecBtn.Width = 100
autoExecBtn.Height = 30
…(省略)…
'イベント設定:ボタンクリック時、サイト遷移開始
AddHandler autoExecBtn.Click,
Sub()
'検索文字はひなまつり
Dim setword As String = "ひなまつり"
'入力対象要素を指定して入力実行
Call webview.ElementInput(tag:="INPUT",
classname:="gLFyf gsfi",
id:="",
innertext:="",
setword:=setword)
'クリック対象要素を指定してClick実行
Call webview.ElementClick(tag:="INPUT",
classname:="gNO89b",
id:="",
innertext:="")
End Sub
Catch ex As Exception
Debug.Print("[Error] : " & Now & " : " & ex.ToString)
End Try
End Sub
検索は無事成功。
まだまだ触りたいWebview2。
次回はマウスジェスチャーや右クリックメニューのカスタムなんかを試したい。
自分用に使いやすいオリジナルブラウザソフトなんか作れると満足。