2013-03-03 36 views
11

我一直在玩弄使用VBS/VBA从网页上抓取数据。在HTMLElement上使用getElementById而不是HTMLDocument

如果是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 

我一直在寻找的HTMLElement.document性能,看它是否是像文件的一个片段,但其无论是困难的工作,或只是心不是我所想的

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

这也似乎是一个啰嗦的方式来做到这一点(虽然这通常是vba imo的方式)。 任何人都知道是否有一种更简单的方法来链接功能?

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

我给一个去,我想类似的东西,浏览到我的网页,然后导航到一个'javascript:'url。工作,但不是很好。你知道'Document.parentWindow.ExecScript'是否阻塞吗?或者可能我的脚本在结果设置之前没有完成执行? (也会在一秒钟内测试自己)。我仍然想知道是否有一种方法可以纯粹用VB来完成它! – NickSlash 2013-03-03 23:49:53

+0

这并不是说querySelector不能与IE9 + dll一起工作,我只是没有测试过那些 – mkingston 2013-03-03 23:50:44

+0

@NickSlash我编辑了我的答案以回应您的评论。至于阻挡,我认为是这样,但我不确定。应该很容易测试(嵌套循环计数到2^31或JS中的最大整数)。 – mkingston 2013-03-04 00:02:05

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

由于DEE上面与刮()子程序答案。代码完全按照书面形式工作,然后我能够将代码转换为与我正在尝试抓取的特定网站一起工作。

我没有足够的声誉给予好评或发表评论,但我的确有一些细微的改进,添加到DEE的回答是:

  1. 你需要通过“工具\引用添加VBA参考“to”微软HTML对象库,以便代码编译。

  2. 我注释掉了浏览器。可见线和添加的注释如下

    'if you need to debug the browser page, uncomment this line: 
    'Browser.Visible = True 
    
  3. 我添加了一行浏览器设置之前关闭浏览器=无:

    Browser.Quit 
    

再次感谢D形电极!

ETA:这适用于使用IE9的机器,但不适用于使用IE8的机器。任何人都有解决办法?

自己找到了修复,所以回到这里来发布它。 ClassName函数在IE9中可用。对于这个在IE8的工作,您使用querySelectorAll,与对象,你的类名前面的点都在寻找:

'Set repList = doc.getElementsByClassName("reportList") 'only works in IE9, not in IE8 
Set repList = doc.querySelectorAll(".reportList")  'this works in IE8+ 
相关问题