2013-03-03 19 views
11

Ich habe mit Scraping Daten von Webseiten mit VBS/VBA gespielt.Verwenden Sie GetElementById auf HTMLElement anstelle von HTMLDocument

Wenn es Javascript wäre ich weg sein, wie es einfach ist, aber es scheint nicht ganz so einfach in VBS/VBA zu sein.

Dies ist ein Beispiel, das ich für eine Antwort gemacht, es funktioniert, aber ich hatte auf dem Zugriff auf die untergeordneten Knoten geplant getElementByTagName verwenden, aber ich kann nicht herausfinden, wie sie zu benutzen! Das Objekt HTMLElement verfügt nicht über diese Methoden.

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 

Ich habe am HTMLElement.document Eigenschaft suchen, zu sehen, wenn es wie ein Fragment des Dokuments ist aber die entweder schwer zu verarbeiten oder ist nicht genau das, was ich denke,

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

Dies scheint auch ein langatmigen Weg, es zu tun (obwohl das normalerweise der Weg für VBA Imo ist). Wer weiß, ob es eine einfachere Möglichkeit gibt, Funktionen zu verketten?

Document.getElementById("target").getElementsByTagName("tr") wäre genial ...

Antwort

4

ich es auch nicht mögen.

So Javascript verwenden:

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 

Lassen Sie mich wissen, wenn Sie mit dieser Probleme haben; Ich habe den Kontext von einer Methode zu einer Funktion geändert.

Übrigens, welche Version von IE verwenden Sie? Ich vermute, du bist auf < IE8. Wenn Sie auf IE8 aktualisieren, nehme ich an, dass es shdocvw.dll zu iframe.dll aktualisiert und Sie können document.querySelector/All verwenden.

bearbeiten

Kommentar Antwort, die nicht wirklich ein Kommentar: Grundsätzlich ist die Möglichkeit, dies in VBA zu tun ist, die untergeordneten Knoten zu durchqueren. Das Problem ist, dass Sie nicht die richtigen Rückgabetypen erhalten. Sie können dies beheben, indem Sie eigene Klassen erstellen, die IHTMLElement und IHTMLElementCollection (separat) implementieren; aber das ist viel zu schwer für mich, es zu tun, ohne bezahlt zu werden :). Wenn Sie entschlossen sind, lesen Sie das Implementierungsschlüsselwort für VB6/VBA.

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

Ich gebe, dass ein zu gehen, habe ich versucht, etwas Ähnliches zu meiner Seite navigieren dann Navigiere zu einer 'javascript:' URL. gearbeitet, aber nicht sehr gut. Weißt du, ob 'Document.parentWindow.ExecScript' blockiert? Oder könnte mein Skript nicht fertig ausgeführt werden, bevor das Ergebnis gesetzt ist? (testet mich auch in einer Sekunde). Ich würde immer noch gerne wissen, ob es einen Weg gibt, es nur mit VB zu machen! – NickSlash

+0

Das soll nicht heißen, dass querySelector nicht mit den IE9 + dlls funktioniert, ich habe diese nicht getestet – mkingston

+0

@NickSlash Ich habe meine Antwort bearbeitet, um auf Ihren Kommentar zu antworten. Was das Blockieren betrifft, denke ich, aber ich bin mir nicht sicher. Sollte ziemlich einfach zu testen sein (paar verschachtelte Schleifen zählen zu 2^31 oder was auch immer die ganze Zahl in JS ist). – 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

Dank dee für die Antwort oben mit dem Scrape() Unterprogramm. Der Code funktionierte perfekt wie geschrieben, und ich konnte den Code so konvertieren, dass er mit der spezifischen Website arbeitete, die ich zu scrappen versuche.

Ich habe nicht genug Ruf upvote oder zu kommentieren, aber ich habe tatsächlich ein paar kleineren Verbesserungen an dee Antwort hinzuzufügen:

  1. Sie die VBA-Referenz über „Tools \ Referenzen hinzufügen müssen "to" Microsoft HTML-Objektbibliothek, damit der Code kompiliert werden kann.

  2. Ich habe den Browser auskommentiert.Sichtbare Linie und hinzugefügt, um den Kommentar wie folgt

    'if you need to debug the browser page, uncomment this line: 
    'Browser.Visible = True 
    
  3. Und ich fügte eine Linie, die den Browser vor Set-Browser schließen = Nothing:

    Browser.Quit 
    

Thanks again dee!

ETA: Dies funktioniert auf Maschinen mit IE9, aber nicht Maschinen mit IE8. Jeder hat eine Lösung?

Ich fand die Lösung selbst, also kam ich hierher zurück, um es zu posten. Die ClassName-Funktion ist in IE9 verfügbar. Dazu in IE8 zu umgehen, verwenden Sie querySelectorAll, mit einem Punkt den Klassennamen des Objekts vorhergehenden Sie suchen:

'Set repList = doc.getElementsByClassName("reportList") 'only works in IE9, not in IE8 
Set repList = doc.querySelectorAll(".reportList")  'this works in IE8+ 
Verwandte Themen