2013-03-03 7 views
11

Ho giocato con i dati di scraping delle pagine Web utilizzando VBS/VBA.Utilizzare getElementById su HTMLElement anziché HTMLDocument

Se fosse Javascript, mi piacerebbe essere semplice, ma non sembra essere abbastanza semplice in VBS/VBA.

Questo è un esempio che ho fatto per una risposta, funziona ma avevo programmato di accedere ai nodi figlio usando getElementByTagName ma non riuscivo a capire come usarli! L'oggetto HTMLElement non ha quei metodi.

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 

ho cercato presso la struttura HTMLElement.document, vedere se è come un frammento del documento, ma la sua sia difficile lavorare con o semplicemente isnt quello che penso

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

Sembra che questa anche una lungo cammino tortuoso per farlo (anche se questo di solito è il modo di vba imo). Qualcuno sa se esiste un modo più semplice per concatenare le funzioni?

Document.getElementById("target").getElementsByTagName("tr") sarebbe fantastico ...

risposta

4

non mi piace neanche.

Così utilizzare 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 

fatemi sapere se avete problemi con questo; Ho cambiato il contesto da un metodo a una funzione.

A proposito, quale versione di IE stai usando? Sospetto che tu sia nello < IE8. Se si esegue l'aggiornamento a IE8 suppongo che aggiornerà shdocvw.dll in ieframe.dll e si sarà in grado di utilizzare document.querySelector/All.

Modifica

commento di risposta che non è davvero un commento: Fondamentalmente il modo per farlo in VBA è quello di attraversare i nodi figlio. Il problema è che non ottieni i tipi di ritorno corretti. Puoi risolvere questo problema creando le tue classi che (separatamente) implementano IHTMLElement e IHTMLElementCollection; ma questo è MODO troppo doloroso per me di farlo senza essere pagato :). Se sei determinato, vai a leggere la parola chiave Implements per 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

Darò che un andare, ho provato qualcosa di simile, accedere alla mia pagina poi vai a un 'javascript:' url. ha funzionato, ma non molto bene. Sai se 'Document.parentWindow.ExecScript' sta bloccando? o il mio script potrebbe non terminare l'esecuzione prima che il risultato sia impostato? (mi metterò alla prova anche in un secondo). Mi piacerebbe comunque sapere se c'è un modo per farlo puramente con VB! – NickSlash

+0

Questo non vuol dire querySelector non funzionerà con le DLL IE9 +, non ho appena testato quelli – mkingston

+0

@NickSlash Ho modificato la mia risposta per rispondere al tuo commento. Per quanto riguarda il blocco, penso di sì, ma non ne sono sicuro. Dovrebbe essere abbastanza facile da testare (un paio di loop nidificati che contano fino a 2^31 o qualunque sia il numero intero massimo in 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

Grazie a Dee per la risposta di cui sopra con la subroutine Scrape(). Il codice ha funzionato perfettamente come scritto e sono riuscito a convertire il codice in modo che funzionasse con il sito Web specifico che sto cercando di analizzare.

non ho abbastanza reputazione per upvote o di commentare, ma io in realtà hanno alcuni piccoli miglioramenti da aggiungere alla risposta di dee:

  1. Sarà necessario aggiungere il riferimento VBA tramite "Strumenti \ Riferimenti "a" Microsoft HTML Object Library per la compilazione del codice

  2. Ho commentato il browser.linea visibile e ha aggiunto il commento come segue

    'if you need to debug the browser page, uncomment this line: 
    'Browser.Visible = True 
    
  3. E ho aggiunto una linea di chiudere il browser prima Set Browser = niente:

    Browser.Quit 
    

Grazie ancora dee!

ETA: funziona su macchine con IE9, ma non su macchine con IE8. Qualcuno ha una soluzione?

Ho trovato la soluzione da solo, quindi sono tornato qui per postarlo. La funzione ClassName è disponibile in IE9. Per far funzionare tutto questo in IE8, si utilizza querySelectorAll, con un punto che precede il nome della classe dell'oggetto che si sono alla ricerca di:

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