2013-03-03 11 views
11

Bawiłem się ze skrobaniem danych ze stron internetowych przy użyciu VBS/VBA.Użyj getElementById na HTMLElement zamiast HTMLDocument

Gdyby to był Javascript, nie byłoby mi łatwo, ale nie wydaje się być tak proste w VBS/VBA.

Oto przykład, który zrobiłem dla odpowiedzi, działa, ale planowałem dostęp do węzłów potomnych przy użyciu getElementByTagName, ale nie mogłem dowiedzieć się, jak z nich korzystać! Obiekt HTMLElement nie ma tych metod.

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 

czekałem na miejscu HTMLElement.document, sprawdzając, czy jest to jak fragment dokumentu, ale jest też trudne do pracy lub po prostu isnt co myślę

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

Wydaje się to również długi sposób, aby to zrobić (chociaż to zwykle sposób dla vba imo). Ktoś wie, czy istnieje prostszy sposób na łańcuchowe funkcje?

Document.getElementById("target").getElementsByTagName("tr") byłoby super ...

Odpowiedz

4

nie lubię go albo.

więc używać 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 

Daj mi znać, jeśli masz jakieś problemy z tym; Zmieniłem kontekst z metody na funkcję.

Nawiasem mówiąc, z jakiej wersji IE korzystasz? Podejrzewam, że jesteś na < IE8. Jeśli zaktualizujesz program do wersji IE8, zakładam, że zaktualizuje on plik shdocvw.dll do pliku ieframe.dll, a będziesz mógł używać document.querySelector/All.

Edit

Komentarz odpowiedź, która nie jest to komentarz: Zasadniczo sposób to zrobić w VBA jest przemierzać węzły potomne. Problem polega na tym, że nie otrzymujesz poprawnych typów zwrotu. Można to naprawić, tworząc własne klasy, które (oddzielnie) implementują IHTMLElement i IHTMLElementCollection; ale to WAY zbyt dużo bólu dla mnie, aby to zrobić bez otrzymywania zapłaty :). Jeśli jesteś zdeterminowany, idź i przeczytaj słowo kluczowe Implements dla 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

dam, że Go, próbowałem coś podobnego, przejdź do mojej strony następnie przejdź do adresu URL 'javascript:'. pracował, ale niezbyt dobrze. Czy wiesz, czy 'Document.parentWindow.ExecScript' jest blokowany? lub czy mój skrypt nie może zostać zakończony przed ustawieniem wyniku? (sprawdzę się też za chwilę). Nadal chciałbym wiedzieć, czy istnieje sposób, aby zrobić to wyłącznie z VB! – NickSlash

+0

To nie znaczy, querySelector nie będzie działał z bibliotekami IE9 +, po prostu nie przetestowałem tych – mkingston

+0

@NickSlash Edytowałem swoją odpowiedź, aby odpowiedzieć na twój komentarz. Jeśli chodzi o blokowanie, tak myślę, ale nie jestem pewien. Powinien być dość łatwy do przetestowania (kilka zagnieżdżonych pętli liczących do 2^31 lub cokolwiek, co całkowita liczba całkowita jest w 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

Dzięki Dee za odpowiedzi powyżej z Zeskrobać (podprogramu). Kod działał idealnie w formie pisemnej, a następnie udało mi się przekonwertować kod do pracy z konkretną witryną, którą próbuję skrobać.

nie mam wystarczającej reputacji upvote lub wypowiedzenia, ale w rzeczywistości mają kilka drobnych ulepszeń, aby dodać do Dee odpowiedzi:

  1. Musisz dodać odwołanie VBA poprzez „Narzędzia \ Referencje "do" Biblioteki obiektów HTML HTML w celu skompilowania kodu. "

  2. Skomentowałem przeglądarkę.Widoczna linia i dodał komentarz następująco

    'if you need to debug the browser page, uncomment this line: 
    'Browser.Visible = True 
    
  3. I dodałem linię zamknąć przeglądarkę przed ustawieniem Browser = Nothing:

    Browser.Quit 
    

Dzięki ponownie dee!

ETA: działa to na maszynach z IE9, ale nie na maszynach z IE8. Ktoś ma poprawkę?

Znalazłem poprawkę sam, więc wróciłem tutaj, aby ją opublikować. Funkcja ClassName jest dostępna w IE9. Aby to działało w IE8, należy użyć querySelectorAll, z kropką poprzedzający nazwę klasy obiektu szukasz:

'Set repList = doc.getElementsByClassName("reportList") 'only works in IE9, not in IE8 
Set repList = doc.querySelectorAll(".reportList")  'this works in IE8+ 
Powiązane problemy