如何使用XML HTTP请求在Visual Basic for Applications中提取Web数据?

时间:2019-04-09 15:24:20

标签: excel vba dom web-scraping xmlhttprequest

  

版本:Microsoft Visual Basic for Applications 7.1

我正在从事小型数据挖掘/网络数据提取个人项目。我的问题是关于数据提取的。

使用IE从网页中提取数据是可行的,但运行速度非常慢,因此,我倾向于使用XML HTTP请求。但是,当我在打算使用的网站上试用时,除了一些静态内容之外,我无法提取所需的数据。检查响应文本后,我发现它不包含我需要的数据。可能是由JavaScript或类似技术生成的。我不确定这些脚本是否像在Web浏览器中一样使用XML HTTP请求在VBA中呈现。

此外,在这里值得注意的是从开发人员工具>网络检查网页时,它提供了一个Request URL,其中响应包含我需要的大多数数据,但它是JSON格式。我不知道如何解析它,但我只是提供了此信息,因此,以防无法使用XML HTTP请求从动态网页提取数据,您可能会指出正确的方向。

我希望您可以花几分钟查看我的代码以及我在哪里可能做错了代码。

非常感谢大家。非常感谢您的帮助。

这是我要做什么的基本概念:

使用XML(无法提取所需的数据):

Option Explicit

Sub dataMinExProject_XML()

    Dim xmlPage As MSXML2.XMLHTTP60
    Dim htmlDoc As MSHTML.HTMLDocument
    Dim coName As MSHTML.IHTMLElement
    Dim secSym As MSHTML.IHTMLElement
    Dim closePrice As MSHTML.IHTMLElement
    Dim URL As String

    URL = "https://www.pse.com.ph/stockMarket/companyInfo.html?id=260&security=468&tab=0"

    Set xmlPage = New MSXML2.XMLHTTP60
    With xmlPage
        .Open "POST", URL, False
        .send
    End With

    Do Until xmlPage.ReadyState = 4
        DoEvents
    Loop

    Set htmlDoc = New MSHTML.HTMLDocument
    htmlDoc.body.innerHTML = xmlPage.responseText

    Set coName = htmlDoc.getElementById("comTopInfoHead").Children(0)
    Set secSym = htmlDoc.getElementById("secSymbol")
    Set closePrice = htmlDoc.getElementById("headerLastTradePrice")

    Debug.Print "Company Name: ", """" & coName.innerText & """"
    Debug.Print "Security Symbol: ", """" & secSym.innerText & """"
    Debug.Print "Closing Price: ", """" & closePrice.innerText & """"

    xmlPage.abort
    Set xmlPage = Nothing
    MsgBox ("alright!")

End Sub

Immediate Window

Company Name:               "BDO Unibank, Inc."
Security Symbol:            ""
Closing Price:              "  "

检查立即窗口后,显示Security SymbolClosing Price未被提取。

为了进行比较,并为了证明要提取的数据存在,我在这里还提供了使用IE实例的代码。

使用IE(提取了数据,但运行速度相对较慢):

Option Explicit

Sub dataMinExProject_IE()

    Dim ieApp As SHDocVw.InternetExplorer
    Dim htmlDoc As MSHTML.HTMLDocument
    Dim coName As MSHTML.IHTMLElement
    Dim secSym As MSHTML.IHTMLElement
    Dim closePrice As MSHTML.IHTMLElement
    Dim URL As String

    URL = "https://www.pse.com.ph/stockMarket/companyInfo.html?id=260&security=468&tab=0"

    Set ieApp = New SHDocVw.InternetExplorer
    With ieApp
        .Navigate (URL)
        .Visible = vbTrue
    End With

    Do Until ieApp.ReadyState = READYSTATE_COMPLETE
        DoEvents
    Loop

    Set htmlDoc = ieApp.Document

    Set coName = htmlDoc.getElementById("comTopInfoHead").Children(0)
    Set secSym = htmlDoc.getElementById("secSymbol")
    Set closePrice = htmlDoc.getElementById("headerLastTradePrice")

    Do Until secSym.innerText <> vbNullString And closePrice.innerText <> vbNullString
        Loop
    DoEvents

    Debug.Print "Company Name: ", """" & coName.innerText & """"
    Debug.Print "Security Symbol: ", """" & secSym.innerText & """"
    Debug.Print "Closing Price: ", """" & closePrice.innerText & """"

    ieApp.Quit
    Set ieApp = Nothing
    MsgBox ("alright!")

End Sub

Immediate Window

Company Name:               "BDO Unibank, Inc."
Security Symbol:            "BDO"
Closing Price:              "130.50"

查看立即窗口,它表明已成功提取数据。但是,正如我之前所说的那样,它的糟糕表现使我不得不考虑其他选择。

  

参考文献:

     

Web Scraping of Masked URL Using VBA

2 个答案:

答案 0 :(得分:0)

处理HTTP请求是一种方法。如您所说,IE浏览器运行缓慢且效率低下。

找到响应后返回感兴趣的数据的请求后,您的工作就相对容易了,并且很可能涉及以下其中一项:

  1. 该请求返回一个html页面作为响应。在这种情况下,您应该使用Microsoft HTML Object Library,将响应HTML分配给HTMLDocument,然后使用现有方法解析对象。为此,您需要引用Microsoft HTML Object Library
  2. 请求返回一个JSON字符串。在这种情况下,您可以将响应存储在字符串变量中,然后使用VBA JSON将其解析为json对象。链接中给出的说明和示例非常有用。使用在线JSON查看器了解响应的结构,您将能够提取所需的任何信息。为此,您当然需要引用Microsoft Scripting Runtime以及VBA JSON模块。
  3. 两者的结合。例如,某些请求可能会返回HTML页面作为响应,其中包含JSON格式的数据。其他人可能返回JSON字符串,并且其中包含的一项可能是HTML表。在这种情况下,可以结合使用上述工作流程。

就请求本身而言,请确保使用对请求必不可少的标头。标头Content-Type:是其中之一,它对于POST请求是必不可少的。您可以使用.setRequestHeader方法。包含请求参数的请求正文也很重要。我建议您使用WinHTTP Services, version 5.1进行请求。

一旦掌握了这些知识,就可以完全控制要检索的数据。

答案 1 :(得分:0)

有一组APIs。目前,Stock API端点似乎无法正常工作。我提出了一个问题。如果它可以再次工作,则可以使用以下语法。 json解析器为jsonconverter.bas。您将.bas添加到您的项目中,然后转到VBE>工具>参考>添加对Microsoft Scripting运行时的引用

Option Explicit
Public Sub dataMinExProject_XML()
    Dim xmlPage As MSXML2.XMLHTTP60, aDate As String, symbol As String, json As Object, url As String
    Set xmlPage = New MSXML2.XMLHTTP60
    aDate = Format$(Date - 1, "MM-DD-YYYY")
    symbol = "JFC"
    url = "http://pseapi.com/api/Stock/" & symbol & "/" & aDate

    With xmlPage
        .Open "GET", url, False
        .send
        Set json = JsonConverter.ParseJson(.responseText)
    End With
End Sub

它返回一个字典对象,您可以通过按键解析信息

{
    "symbol":"JFC",
    "date":"15/03/2017",
    "open":197.0000,
    "high":197.4000,
    "low":195.0000,
    "close":196.0000,
    "bid":195.5000,
    "ask":196.0000,
    "volume":141740,
    "value":27747934.0000,
    "netForeign":-6464136.0000
}

因此,以我的示例为例:

Dim key As  Variant
For Each key In json.keys
    Debug.Print key, json(key)
Next