2013-03-03 23 views
11

VBS/VBAを使用してWebページからデータをスクレイピングして遊んでいます。HTMLElementの代わりにHTMLElementのgetElementByIdを使用します。

もし私がJavascriptであれば、私は簡単だと思いますが、VBS/VBAではそれほど単純ではありません。

これは回答のために作成した例ですが、それは動作しますが、getElementByTagNameを使用して子ノードにアクセスする予定でしたが、使用方法がわかりませんでした。 HTMLElementオブジェクトにはこれらのメソッドがありません。

Sub Scrape() 
Dim Browser As InternetExplorer 
Dim Document As HTMLDocument 
Dim Elements As IHTMLElementCollection 
Dim Element As IHTMLElement 

Set Browser = New InternetExplorer 

Browser.navigate "http://www.hsbc.com/about-hsbc/leadership" 

Do While Browser.Busy And Not Browser.readyState = READYSTATE_COMPLETE 
    DoEvents 
Loop 

Set Document = Browser.Document 

Set Elements = Document.getElementsByClassName("profile-col1") 

For Each Element in Elements 
    Debug.Print "[ name] " & Trim(Element.Children(1).Children(0).innerText) 
    Debug.Print "[ title] " & Trim(Element.Children(1).Children(1).innerText) 
Next Element 

Set Document = Nothing 
Set Browser = Nothing 
End Sub 

私はそれは、文書のフラグメントのようなものですが、そのどちらかで動作するか、単に私が

Dim Fragment As HTMLDocument 
Set Element = Document.getElementById("example") ' This works 
Set Fragment = Element.document ' This doesn't 

をどう思うかイマイチするのが難しいが、これも思える場合見て、HTMLElement.documentプロパティを見てきましたそれを行うために長い風に吹かれた方法(しかし、それは通常vbaのための方法ですが)。 機能をチェーンする簡単な方法があれば誰でも知っていますか?

Document.getElementById("target").getElementsByTagName("tr")は素晴らしいだろう...

答えて

4

私はどちらかそれを好きではありません。

だから、JavaScriptを使用します。

Public Function GetJavaScriptResult(doc as HTMLDocument, jsString As String) As String 

    Dim el As IHTMLElement 
    Dim nd As HTMLDOMTextNode 

    Set el = doc.createElement("INPUT") 
    Do 
     el.ID = GenerateRandomAlphaString(100) 
    Loop Until Document.getElementById(el.ID) Is Nothing 
    el.Style.display = "none" 
    Set nd = Document.appendChild(el) 

    doc.parentWindow.ExecScript "document.getElementById('" & el.ID & "').value = " & jsString 

    GetJavaScriptResult = Document.getElementById(el.ID).Value 

    Document.removeChild nd 

End Function 


Function GenerateRandomAlphaString(Length As Long) As String 

    Dim i As Long 
    Dim Result As String 

    Randomize Timer 

    For i = 1 To Length 
     Result = Result & Chr(Int(Rnd(Timer) * 26 + 65 + Round(Rnd(Timer)) * 32)) 
    Next i 

    GenerateRandomAlphaString = Result 

End Function 

は、あなたがこれで何か問題があれば、私に教えてください。コンテキストをメソッドから関数に変更しました。

ところで、どのバージョンのIEをお使いですか?私はあなたが< IE8だと思う。 IE8にアップグレードすると、shdocvw.dllがieframe.dllに更新されると推測され、document.querySelector/Allを使用できるようになります。本当にコメントではありません

編集

コメント応答:基本的に VBAでこれを行う方法は、子ノードを通過することです。問題は正しい戻り値の型を取得しないということです。 IHTMLElementとIHTMLElementCollectionを(個別に)実装する独自のクラスを作成することでこれを修正できます。しかしそれは私が払うことなしにそれをするためのあまりにも多くの痛みです。あなたが決定したら、VB6/VBAのImplementsキーワードを読んでください。スクレイプ()サブルーチンで上記の回答のためのディーへ

Public Function getSubElementsByTagName(el As IHTMLElement, tagname As String) As Collection 

    Dim descendants As New Collection 
    Dim results As New Collection 
    Dim i As Long 

    getDescendants el, descendants 

    For i = 1 To descendants.Count 
     If descendants(i).tagname = tagname Then 
      results.Add descendants(i) 
     End If 
    Next i 

    getSubElementsByTagName = results 

End Function 

Public Function getDescendants(nd As IHTMLElement, ByRef descendants As Collection) 
    Dim i As Long 
    descendants.Add nd 
    For i = 1 To nd.Children.Length 
     getDescendants nd.Children.Item(i), descendants 
    Next i 
End Function 
+0

:これはIE8で動作するためには、あなたが探しているオブジェクトあなたのクラス名の前にドットで、querySelectorAllを使用します'javascript:' urlに移動します。働いたが、あまりうまくいかなかった。 'Document.parentWindow.ExecScript'がブロックしているかどうか知っていますか?結果が設定される前にスクリプトが実行を終了しない可能性がありますか? (秒も自分自身をテストします)。私はまだ純粋にVBでそれを行う方法があるかどうかを知りたいです! – NickSlash

+0

これは、querySelectorがIE9 + dllで動作しないとは限りません。私はそれらをテストしていません。 – mkingston

+0

@NickSlash私はあなたのコメントに答えるために私の答えを編集しました。ブロッキングに関してはそうだと思いますが、私は確信していません。かなり簡単にテストすることができます(ネストされたループの数は2^31まで、または整数の最大値はJSにあります)。 – mkingston

12
Sub Scrape() 
    Dim Browser As InternetExplorer 
    Dim Document As htmlDocument 
    Dim Elements As IHTMLElementCollection 
    Dim Element As IHTMLElement 

    Set Browser = New InternetExplorer 
    Browser.Visible = True 
    Browser.navigate "http://www.stackoverflow.com" 

    Do While Browser.Busy And Not Browser.readyState = READYSTATE_COMPLETE 
     DoEvents 
    Loop 

    Set Document = Browser.Document 

    Set Elements = Document.getElementById("hmenus").getElementsByTagName("li") 
    For Each Element In Elements 
     Debug.Print Element.innerText 
     'Questions 
     'Tags 
     'Users 
     'Badges 
     'Unanswered 
     'Ask Question 
    Next Element 

    Set Document = Nothing 
    Set Browser = Nothing 
End Sub 
0

感謝。コードは書かれているように完璧に機能し、コードを私が掻き集める特定のウェブサイトで動作するように変換することができました。私はupvoteしたり、コメントするのに十分な評判を持っていないが、私は実際にディーの答えに追加するには、いくつかのマイナーな改良を持っているん

  1. あなたがツール」を介してVBAの参照を追加する必要があります\参照"to" Microsoft HTML Object Libraryを使用してコードをコンパイルします。

  2. 私はブラウザをコメントアウトしました。何も=

    'if you need to debug the browser page, uncomment this line: 
    'Browser.Visible = True 
    
  3. を次のように目に見えるラインとは、コメントを追加しましたしないと私はセットのブラウザの前に、ブラウザを閉じるように行を追加しました:

    Browser.Quit 
    

おかげで再びディー!

ETA:これはIE9搭載機では動作しますが、IE8搭載機では動作しません。誰でも修正がありますか?

自分で修正が見つかりましたので、ここに戻って投稿してください。 ClassName関数はIE9で使用できます。私は、その後、私のページに移動Goは、私は似た何かをしようとしたことをあげる

'Set repList = doc.getElementsByClassName("reportList") 'only works in IE9, not in IE8 
Set repList = doc.querySelectorAll(".reportList")  'this works in IE8+ 
関連する問題