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

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

新規登録して質問してみよう
ただいま回答率
85.48%
DOM

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

VBA

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

1回答

1503閲覧

VBAでのスクレイピングです。ステップ実行でうまくいくのですが、走らせるとダメみたいです。

TonyChopper

総合スコア0

DOM

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

VBA

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

1クリップ

投稿2021/05/07 15:46

前提・実現したいこと

ウェブスクレイピングのツールをExcelVBAを使用して作成しています。
class属性の取得部分がうまく機能せず困っています。
前提として、ExcelVBAで実装する必要があります。
このスクレイピングにはブラウザを使用していません。いわゆるDOM操作のみで完結させています。速度やサイト側の条件でその必要があります。

こちらでの質問は初めてでして、無作法がありましたらご容赦ください。

発生している問題・エラーメッセージ

VBAを通常実行すると、class属性の取得ができません。ところが、コードウィンドウでステップ実行するとうまく取得できます。
なので構文のエラー等はないと思っています。走らせると取得できない理由がわかりません。

該当のソースコード

全体は非常に長いので、該当の機能部分を載せます。これは私が定義したクラスのメソッドとして機能します。(クラスモジュール内です)
このメソッドは指定する名称(KeyWord)のclass属性を掴んできて返す機能です。

Public Function GetClassElements(resDataNo As Long, KeyWord As String) As Variant 'ForEachコールバック Dim El As Variant '配列バッファ Dim bufV() As Variant 'データ数初期化 resDataNo = 1 '(※2)ここで止めてもうまくいきます(後述) '全要素ループしてClassNameを評価 For Each El In hDOC.all         '(※1)この部分にいくつかのアプローチ(後述) Debug.Print El.ClassName '←チェック用に入れてみました 'クラス名を比較 If El.ClassName = KeyWord Then '←問題の部分 '結果配列再宣言 ReDim Preserve bufV(1 To resDataNo) '値セット Set bufV(resDataNo) = El 'データ数+1 resDataNo = resDataNo + 1 End If Next El 'データ数修正 resDataNo = resDataNo - 1 '戻り値セット GetClassElements = bufV Set El = Nothing End Function

試したこと

※1の部分に、DoEventsとSleep(50~500くらいまで試しました)をそれぞれ単独、又は一緒に入れてみましたが、結果は変わりませんでした。
また、このメソッドを外部から呼び出す部分に、

DoEvents Variant型 = 当クラス.GetClassElements(resN,KeyWord)

等してみましたが、改善されませんでした。

また、Class属性の取得部分(問題の部分)で、

El.getAttribute("ClassName") = KeyWord '←全てNullが返される El.getAttribute("class") = KeyWord '←同上

上記のようにも試しました。

補足情報(FW/ツールのバージョンなど)

まず、このクラスはHTMLをウェブから取得して、内部に保持するクラスです。そのデータ本体は hDoc です。コンストラクタは定義していません。
HTMLの取得部分は同クラス内の別のメソッドで、

Public Function OpenURL(ByVal strURL As String) As Boolean On Error GoTo Err_OpenURL '関数値初期化 OpenURL = False 'Documentオブジェクト生成 Set hDOC = CreateObject("htmlfile") 'サイトデータの格納先 Dim objHTML As Object 'XMLHTTPオブジェクトを生成 Set objHTML = CreateObject("MSXML2.XMLHTTP") 'URL読み込み With objHTML 'URL読み込み .Open "GET", strURL, False .send '安全用のWait Do While .readyState <> 4 DoEvents Loop 'DOM操作を行える様に変換 hDOC.write .responseText End With '成功 OpenURL = True 'エラー終了 Err_OpenURL: Set objHTML = Nothing End Function

となります。hDocを操作する他のメソッドは全て正常に機能していますので、HTMLの取得自体はうまくいっていると考えています。

チェック用に入れた、Debug.Print El.ClassName の部分を確認したところ、
コードを走らせた(通常実行)場合→全て""が返されているようでした。
コードウィンドウでステップ実行した場合→正しくすべてのClass属性の名前が返されていました。

ステップ実行時にうまくいくという部分ですが、※2の部分でコードを中断し、その後最後まで走らせた場合もうまく取得できていました。

いろいろ調べてみましたが、お手上げです。どなたかご教示いただけると助かります。

環境
Win10 64bit
Excel2019 64bit

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

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

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

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

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

itagagaki

2021/05/07 16:41

コードを走らせてみましたが、取得できましたよ。 Win10 Pro x64 / Office2013 Excel で、シートにボタンを置いて Sub ボタン1_Click() Call OpenURL("https://teratail.com/") Call GetClassElements(1, "hoehoe") End Sub で、ボタンを押すと、Debug.Print El.ClassName でクラス名がぽつぽつイミディエイトウィンドウに出力されました。ただ、空行(つまり"")は多いですね。
TonyChopper

2021/05/07 23:16

早速ご検証いただきましてありがとうございます。クラス属性が存在する時だけクラス名が返されますので、仰る通り正しく機能していると思います。 環境に依存している可能性を検討してみます。
guest

回答1

0

自己解決

プロジェクトのコード全体にDoEventsをばら撒きまくったら、とりあえず正常に動作するようになりました。
これから一つずつ取り除いて原因を特定したいと思います。
コメントをくださった方、検証をいただいた方には大変感謝申し上げます。

投稿2021/05/08 08:27

TonyChopper

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問