2021年5月30日日曜日

tinySeleniumBasicによるEdgeのscraping

 今までVBAで、IEのscrapingを行っていたのですが、IEのsupport期限も迫ってきているのでedgeによるscrapingを検討しました。

参考にしたのは以下です。
 Excel VBAでSeleniumBasicを使わずにスクレイピングする - Qiita

一般的にはseleniumBasicを使用するようですが、他の人に渡すときに色々installしてもらう必要があると厄介なので、MedgeDriver以外はVBAで対応できるこの手法を選択しました。

やることはざっと以下のような内容です。

1) 指定のURLが開いていたらそのwindowを取得、開いていなければ開く。
2) buttonをいくつか押して所望のpageに遷移して情報を読み出す。
3) buttonを押して開いたwindowの情報を読み出す。
4) download buttonを押して、csv fileを開く。

これが全てできればよいのですが、1)の"開いていたらwindowを取得"がまだできません。
その他については何とかなったので、方法などについて簡単にまとめてみます。

a) tinySeleniumBasicのimport
 単純にimportするだけだと思っていたのですがうまくいかず。
 tinySeleniumBasicは拡張子が.clsなのですけどimportするとなぜか標準moduleになってしまいます。
 class moduleを作成して内容をコピペして最初の4行を削除し、object名も変更する必要があります。JsonConverterは拡張子.basで問題なし。

b) 開いているwindowの取得
 tinySeleniumBasicのWebDriverに、COM_GET_ALL_SESSIONSという記述があり、これを使えば何とかなるかと思ったのですが、W3Cの仕様規定には見つからず、実装されていないようです。いろいろ調べてみたところ、自動起動した以外のsession IDはsecurityの観点で取得できなようになっているようです。なのでやむを得ず諦めです。

c) clickで開いたwindowの取得
 これもダメなのかと思いましたが、自動clickで開いたwindowはsession IDが変わらず、window handleを取得して切り替えることが可能でした。

    Dim parameters As Dictionary    '定義せずにsetだけだと型違いになりNG
    Set parameters = New Dictionary
    parameters.Add "handle", ""     'dictionary変数のkeyを確保
    oldWindowHandle =                  Driver.Execute(Driver.CMD_W3C_GET_CURRENT_WINDOW_HANDLE) '親のwindow handleを取得
    For Each objEL In Driver.FindElements(By.Tagname, "BUTTON")
        If objEL.GetText = "ボタンに表示されているテキスト" Then
            objEL.Click '開く
            Application.Wait Now() + TimeValue("00:00:01")  '少し待つ(無くてよさそう)
            NewWindowHandle = Driver.Execute(Driver.CMD_W3C_GET_WINDOW_HANDLES)(2)  'windowhandleを取得、1が親で2が子
    parameters("handle") = NewWindowHandle    'windowhandleset
    Driver.Execute Driver.CMD_SWITCH_TO_WINDOW, parameters    'windowswitch
    Application.Wait Now() + TimeValue("00:00:02")  '少し待つ(無いと止まる)
    'ここで子windowの必要な内容を抽出
    '抽出が終了
    Driver.FindElements(By.Tagname, "BUTTON")(0).Click    'windowを閉じる
    parameters("handle") = oldWindowHandle    'windowhandleset
    Driver.Execute Driver.CMD_SWITCH_TO_WINDOW, parameters    'windowswitch

といった感じです。

d) fileをdownloadして開く

 IE+VBAでは、Automationを使って開くbuttonを押してfileが開くのを待っていましたが、Edgeではbuttonを押すとそのままdownload folderに落ちるので、download folderにfileができるまで待って開けばOK。IEより簡単でした。

おかげさまで必要なことは一通りできるようになりました。
@uezoさんとTim Hallさんに感謝します。

あと、こうだったら良いな、というのを書いておきます。

i) FindElementsで取得したitem数が知りたい

 item数のpropertyが欲しいです。
 for eachで回せばよいのですが、item数0だった場合errorになるし。

ii) FindElementsのby.にallが欲しい。

 ClassName、CssSector、ID、Name、TagNameが使えるのですが、 scrapingしているpageが右click禁止設定になっており、開発者toolでの調査ができません。
 IEではdocument.allで調べており、今回はIE版の焼き直しなので情報は揃っていましたが、最初からEdgeで分析しようとするとallがないと厳しいです。

iii) 検索ボタンが操作可能でないときの回避方法

 @uezoさんの記事の”これは検索ボタンが操作可能でないときにクリック指示を出してしまっていることが原因で、この回避方法についてはまた別途記事にしたいと思います。”を期待しています。waitをかますだけだといまいち心配なので。

以上です。