質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

3回答

26365閲覧

ExcelVBAでIE操作中にIEオブジェクトがクライアントから切断される?

dam9806

総合スコア21

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

0クリップ

投稿2018/12/04 15:20

以下のコードは、あるWebサイトについて3パターンでページをロードするExcelVBAの処理です。

#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_getKatsuki() Dim ret Dim objIE As InternetExplorer ' IEの起動 Set objIE = CreateObject("InternetExplorer.Application") objIE.Visible = True 'パターン1 ret = getKatsuki("9784344033856", objIE) Debug.Print "戻り値=[" & ret & "]" 'パターン2 ret = getKatsuki("9784897481159", objIE) Debug.Print "戻り値=[" & ret & "]" 'パターン3 ret = getKatsuki("4866510553", objIE) Debug.Print "戻り値=[" & ret & "]" objIE.quit Set objIE = Nothing End Sub Function getKatsuki(searchKey As String, objIE As InternetExplorer) Dim url On Error GoTo Err url = "http://www.katsuki-books.jp/kensaku/detail.asp?Cd=" & searchKey objIE.Navigate2 url ' ★ロード待ち Do While objIE.Busy = True Or objIE.readyState <> 4 DoEvents Sleep (1000) '1秒待機 Loop Debug.Print objIE.LocationURL getKatsuki = 1 Exit Function Err: Debug.Print "Err.Number=[" & Err.Number & "], Err.Description=[" & Err.Description & "]" getKatsuki = -1 End Function

これを実行するとパターン1までは正常に動作しますが、パターン2のページロードでビジー状態が継続し、「★ロード待ち」とコメントしているループ処理を脱出しません。(IEを見るとロードは完了しているように見えるのですが・・・)

また、何度か実行していると、「'Navigate2' メソッドは失敗しました: 'IWebBrowser2' オブジェクト」のエラーメッセージを吐くこともあります。

上記コードは最小限の処理のみ記述したものです。実際にはデータ取得する処理等あるのですが、その実行時に「起動されたオブジェクトはクライアントから切断されました。」のエラーが出たこともあります。私は、上のエラーもクライアントから切断されたものと予想しています。

一方で、デバッグでステップ実行すると、最後まで問題なく処理できます。

サイト側で何か制限をかけているのかな・・・と勘ぐったりもしますが分かりません。
原因・対策など教えていただけないでしょうか。

実行環境:Windows7、Excel2016

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

自己解決

objIE.Visible をTrueからFalseに変えたところ、正常に処理できるようになりました。
複数回実行してもエラーにならないので、恐らくこれで大丈夫かと思います。
回答してくださった方、ありがとうございました!

投稿2018/12/06 12:52

dam9806

総合スコア21

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nineplus

2019/01/26 04:15

私もまったく同じ問題にぶちあたり、ここにたどり着きました。 しかし、原因については、いまだにわからずにいます。 デバッグする際は、IEの画面を見ながらテストしたいので、 Trueのままでも、切断されずに正常に動作させるには、どうしたらいいんでしょうね?
dam9806

2019/01/27 00:45

質問主です。 私の質問に対する回答者との過去のやりとりをお読みになり、回答者の提示した処方をお試し下さい。 回答者から色々アドバイスを頂きました。例えば、DoEventsを所々に挿入するなどです。
guest

0

IE表示待ちでしつこくステータスを確認してはどうでしょうか。

VBA

1Function ie_wait(objIE As Object) As Boolean 2 Dim timeout As Date 3 Dim cnt As Integer 4 5 '読み込み中なら以下を繰り返す 6 cnt = 0 7 timeout = DateAdd("s", 50, Now()) 8 Do 9 '下記ステータス30回一致 10 If objIE.Busy = False And objIE.readyState = 4 Then 11 DoEvents 12 Sleep 100 13 DoEvents 14 cnt = cnt + 1 15 If cnt > 30 Then 16 Exit Do 17 End If 18 Else 19 cnt = 0 20 End If 21 22 'タイムアウトチェック 23 If timeout < Now() Then 24 ie_wait = False 25 Exit Function 26 End If 27 DoEvents 28 Loop 29 30 ie_wait = True 31End Function

また、Sleep関数は 64bitでもLongで渡します。
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As Long)

<追記>
私が普段使っているIE表示待ちも試してみてください。
これでダメならお役に立てそうにありません。

VBA

1Public Sub Myloop(ByRef ie As Object) 2 3 Dim nowURL As String 4 nowURL = ie.LocationURL 5 6 Dim IEretry As Boolean: IEretry = False 7 Dim RepeatSec As Single: RepeatSec = Timer 8 9On Error GoTo INVALID 10 11 Do While ie.Busy = True Or ie.readyState < 4 'READYSTATE_COMPLETE 12 DoEvents: Sleep 200 '200ms停止 13 14 If (Timer - RepeatSec) > 80 Then 15 Debug.Print ie.readyState 16 MsgBox "サーバーが応答していません。インターネット接続をご確認後、再度実行してください。" 17 ie.Quit 18 End 19 ElseIf (Timer - RepeatSec) > 40 And IEretry = False Then 20 'IEの再読み込みは1回限りとする 21 IEretry = True 22 On Error Resume Next 23 ie.Refresh 24 Debug.Print "ie.Refresh/エラー番号:" & Err.Number & vbNewLine & "エラーの種類:" & Err.Description 25 On Error GoTo INVALID 26 End If 27 Loop 28 29 DoEvents 30 31 Do While ie.document.readyState <> "complete" 32 DoEvents: Sleep 200 '200ms停止 33 34 If (Timer - RepeatSec) > 100 Then 35 Debug.Print ie.document.readyState 36 ie.Quit 37 MsgBox "ドキュメントを取得できません。インターネット接続をご確認後、再度実行してください。" 38 End 39 End If 40 Loop 41 42 '念のため確認 43 DoEvents: Sleep 1: DoEvents 44 If ie.Busy = True Or ie.readyState < 4 Then 45 Call Myloop(ie) '再帰呼出 46 End If 47 48 Exit Sub 49 50INVALID: 51 52 Debug.Print "エラー番号:" & Err.Number & vbNewLine & "エラーの種類:" & Err.Description 53 54 If Err.Number = 70 Then 55 '実行時エラー「書き込みできません。」(70)は想定内なので無視。 56 On Error GoTo 0 57 MsgBox "IE読み込み時にエラー発生。処理を継続します。" 58 59 ElseIf Err.Number = -2147417848 Then 60 On Error GoTo 0 61 '新規IEオブジェクト作成 62 Dim getIE As Object 63 Set getIE = CreateObject("InternetExplorer.Application") 64 getIE.Visible = True 65 getIE.navigate nowURL 'URL指定 66 67 MsgBox "対象IEが存在しないため、IEを起動します。" 68 Set ie = getIE 69 getIE = Nothing 70 71 Else 72 '上記以外はエラーを発生させる。 73 Err.Raise Err.Number 74 End If 75End Sub

投稿2018/12/05 12:58

編集2018/12/05 13:30
TanakaHiroaki

総合スコア1063

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dam9806

2018/12/05 13:22

回答ありがとうございます! ご呈示いただいたコードを盛り込んで3回実行しました。 1回目は3パターンとも全て正常終了しました。 2・3回目は、パターン1は正常終了して、パターン2でタイムアウト、パターン3で「'Navigate2' メソッドは失敗しました: 'IWebBrowser2' オブジェクト」のエラーとなりました。
dam9806

2018/12/05 13:24

なお、実行環境は32bit環境です。
dam9806

2018/12/05 13:46

再度ご呈示いただいたコードを盛り込んで実行しましたが、ほぼ同じ結果となりました。 パターン2の表示待ちで、「サーバーが応答していません。インターネット接続をご確認後、再度実行してください。」のメッセージが出力されました。
dam9806

2018/12/05 13:46

お付き合いくださり、ありがとうございました!
TanakaHiroaki

2018/12/05 14:40 編集

キャッシュを読み込んでいる可能性があるため、各ステップの間に以下およびIE待ち時間処理を入れてみてください。 objIE.Navigate2 "http://www.katsuki-books.jp/?time=" & Format(Now, "yymmddhhnnss")
dam9806

2018/12/06 00:40

Navigate2のステップ1行のみ修正して実行しました。 やはりパターン1は正常処理できて、パターン2で異常となります。 以下のログが吐かれました。 ~~~ここから~~~ http://www.katsuki-books.jp/kensaku/detail.asp?Cd=9784344033856&time=181206091604 戻り値=[1] ie.Refresh/エラー番号:-2147467259 エラーの種類:'Refresh' メソッドは失敗しました: 'IWebBrowser2' オブジェクト 1 ~~~ここまで~~~ キャッシュとおっしゃられたので、Navigateの前にキャッシュを削除する処理を追加したりもしましたが、上記同様のエラーとなりました。 デバッグでのステップ実行は問題なく動くのに、一括実行だと動かないのが解せません。 実行中に唐突にブレークポイントを設定しても、すべて正常に処理できます。
ExcelVBAer

2018/12/06 03:46

一括実行だと途中で動かなくなる(途中からデバッグすると動く)のは、 ブラウザ(IE、Chrome、etc)の仕様だったと思います。 Webから攻撃されないよう、そうなってるものなんだと思っています。
ExcelVBAer

2018/12/06 03:51

自分も詳しい事は知りませんが、自己責任ですので。
dam9806

2018/12/06 05:19

お気遣い、ありがとうございます。 連続実行は控えます。
dam9806

2018/12/06 11:21

objIE.Visible をTrueからFalseに変えたところ、正常に処理できるようになりました。 複数回実行してもエラーにならないので、恐らくこれで大丈夫かと思います。 メモリ等のリソース関係で何か問題が出ていたのかもしれません。 キャッシュを使わないURLの作成方法や、具体的なコードまで提示していただき、とても勉強になりました。 お付き合いくださり、ありがとうございました!
TanakaHiroaki

2018/12/06 12:42

直接お役に立てませんでしたが、解決してよかったですね。 別の下記質問で私が回答した objIE.Visible = False が有効なケースがあることがわかり、うれしいです。 https://teratail.com/questions/102572
guest

0

Sleep の直後に DoEvents 使ってもダメ?

投稿2018/12/05 00:01

ExcelVBAer

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dam9806

2018/12/05 00:41

コメントありがとうございます。上記コードにて、sleepとDoEventsの順序を入れ替えてみましたが、結果は変わりませんでした。
ExcelVBAer

2018/12/05 03:54

自分も詳しくはないですが、サイト側で制限をしているのではなく、 ブラウザ自体(ソフト、と言った方がいい?)が制限があったと思います。 Navigateだけではなく、GetElement等、IEに関わる処理全般的に、 1秒待つ等の待ち時間をとっていないと、 サーバーに対する攻撃とみなされ、 IEが正常に動かなくなると思われます。 「実際にはデータ取得する処理等あるのですが」とありますので、 そこの処理の合間に休み時間を取ってあげてはいかがでしょうか? デバッグでステップ実行すれば処理可能ということは、 同じくらいのテンポで処理を進めていけば 処理可能と予想されます。 ※自分の経験則なので、事実関係は自分で調べてください。
dam9806

2018/12/05 04:55

ありがとうございます。 Navigateの前後で30秒ほどsleepさせたりもしたのですが、変わりませんでした。 一方で、ステップ実行だと、Shift+F8を押しっぱなしにして間をあけることなく実行しても、正常に処理できました。
ExcelVBAer

2018/12/06 00:09

別の角度から聞かせてください。 「実際にはデータ取得する処理等ある」は どういう処理をしているのでしょうか?
dam9806

2018/12/06 00:51

objIE.document.body.outerHTMLでソースを取得して文字列検索により欲しいデータを探して取得たり、DOM操作で欲しいデータのあるノード(?)を検索してデータを取得しています。 このような回答で宜しいでしょうか。
ExcelVBAer

2018/12/06 02:44

その各データ取得の処理前後に、DoEvents や Sleep を挟んでみても解消されないでしょうか?
dam9806

2018/12/06 03:05

IEオブジェクト(objIE)にアクセスするコードの前後に 「DoEvents: Sleep 500」 これを追加して実行しました。 パターン1・2は正常に処理できたのですが、パターン3で 「'Navigate2' メソッドは失敗しました: 'IWebBrowser2' オブジェクト」 のエラーとなりました。
dam9806

2018/12/06 03:17

IEオブジェクトにアクセスする際に、以下のようにメソッドを繋げているのですが、関係ありそうでしょうか。 objIE.document.getElementsByClassName("naiyo")(0).getElementsByTagName("a")(0).Click
ExcelVBAer

2018/12/06 03:43

これも経験則ですが、getElementsByXXX 等、IEにアクセスする回数が多いと、 たまに失敗することがあったと思います。 なので、自分は、getElementsByXXX 等用に関数を作り、 10回トライし、トライ毎に待ち時間を長くして対応したように思います。 メソッドチェーンは書くのは楽ですが、 処理が込み入ってくると安全ではないので、 1回づつに分けた方が無難だと思います。
dam9806

2018/12/06 05:21

ありがとうございます。 対応に時間かかりそうですが、メソッドを分割して試してみたいと思います。
dam9806

2018/12/06 11:22

objIE.Visible をTrueからFalseに変えたところ、正常に処理できるようになりました。 複数回実行してもエラーにならないので、恐らくこれで大丈夫かと思います。 メモリ等のリソース関係で何か問題が出ていたのかもしれません。 DoEventsを所々に挟むというのは、OSに制御渡すことで擬似的にステップ実行している状況に近づけていたのだと思います。とても勉強になりました。 お付き合いくださり、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問