VBAによるIE操作でロード完了判定がうまくいかないときはVisibleを弄ってみる

Excel VBAでIEを操作するときの話。 全てのサイトで・・・というわけではないが、ページ遷移がうまくいかず、ページロード中の状態が継続することがある。
この記事の結論を先に言えば、原因は不明だが、IEオブジェクトのVisibleプロパティをFalseにすることで、スムーズに遷移することができる。

サンプルコード

例えば、以下のようなソースコードを見てみる。 31行目以下がメイン処理(getPageInfo)で、7〜29行目はメイン処理を呼び出すドライバ(driver_getPageInfo)だ。
ドライバがメイン処理を呼び出す際に検索キー(”9784344033856″など)を渡している。getPageInfoの内部では、受け取った検索キーを元にURLを生成して遷移している。※35行目のURLはダミー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)
#Else
Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
#End If

Private Sub driver_getPageInfo()
    Dim ret
    Dim objIE As InternetExplorer

    ' IEの起動
    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Visible = True

    'パターン1
    ret = getPageInfo("9784344033856", objIE)
    Debug.Print "戻り値=[" & ret & "]"

    'パターン2
    ret = getPageInfo("9784897481159", objIE)
    Debug.Print "戻り値=[" & ret & "]"

    'パターン3
    ret = getPageInfo("4866510553", objIE)
    Debug.Print "戻り値=[" & ret & "]"

    objIE.quit
    Set objIE = Nothing
End Sub

Function getPageInfo(searchKey As String, objIE As InternetExplorer)
    Dim url
    On Error GoTo Err

    url = "http://xxx?Cd=" & searchKey
    objIE.Navigate2 url
    ' ★ロード待ち
    Do While objIE.Busy = True Or objIE.readyState <> 4
        DoEvents
        Sleep (1000) '1秒待機
    Loop
    Debug.Print objIE.LocationURL

    getPageInfo = 1
    Exit Function
Err:
    Debug.Print "Err.Number=[" & Err.Number & "], Err.Description=[" & Err.Description & "]"
    getPageInfo = -1
End Function

特定のサイトでは、ロード判定のループを脱出できない

このdriver_getPageInfoを実行してみると、15行目のパターン1は問題なく動作するのだが、パターン2の処理中で36行目のページ遷移処理(Navigate2)を実行後、どういうわけかIEオブジェクトのビジー状態が継続してしまい、38行目のループ処理を脱出できず無限ループ状態になる。(改めて言うが、特定のサイトでのみ発生する事象である)

不思議なことは、IEブラウザを見るとページロードは完了しており、正常に表示されているのだ。 さらには、このプログラムにブレイクポイントを設定しつつステップ実行すると、問題なく38行目のループを脱出できることだ。(連続実行はNGでステップ実行はOKってどういうこと?) ステップ実行でうまくいくので、「制御がOS側にある場合は問題が生じないのかも」と思い、38行目の前後付近に適当にDoEventsを追加してみたが、それでも解決しなかった。

試行錯誤した結果、たまたま解決できた方法が、13行目のobjIE.VisibleをFalseにしたことだ。「メモリに問題があるのかな」と思って上記を試したら、うまくいった。これにて38行目のループを無事に脱出することができ、全ての処理が滞ることなく正常終了した。

デバッグについて

一方で、解決はしたものの、objIE.VisibleをFalseにすることでブラウザが非表示となり、デバッグがしずらくなる。試していないが、ページ遷移をするときだけ一時的にVisibleを変えてみては如何だろうか。つまり、13行目でVisible設定するのは廃止して、代わりに36行目のページ遷移実行前にVisible = Falseに設定し、42行目のループ脱出後に再度Visible = Trueにするわけだ。こうすれば、ブラウザが非表示になるのはページ遷移の最中だけなので、デバッグがしずらくなることはないと思う。

一般化して言えば、VisibleをFalseにすることで解決するような問題が生じた場合は、その問題の原因となるコードの直前でFalseに設定し、直後でVisibleにすれば良いってことだ。実際に試したわけではないが、これでいけそうな気がする。

関連ページ