【问题标题】:How to scrape data from Bloomberg's website with VBA如何使用 VBA 从 Bloomberg 的网站上抓取数据
【发布时间】:2019-05-16 21:19:11
【问题描述】:

背景

免责声明:我是初学者,请公开我的 - 最有可能是错误的 - 代码。

我想使用启用按钮的 VBA 宏来更新货币对的值(PREV CLOSE)。我的 Excel 工作表在 G:G 列 上包含 FX 对(例如 USDGBP),然后用于为该列中的每一对运行 FOR 循环。

然后该值将存储在 I:I 列

现在,调试器的问题在于我将在下面突出显示的一行代码

来源

我从https://www.youtube.com/watch?v=JxmRjh-S2Ms&t=1050s 获得了一些灵感 - 特别是从 17:34 开始 - 但我希望我的代码只需按一下按钮即可在多个网站上运行。

我试过下面的代码

Public Sub Auto_FX_update_BMG()

    Application.ScreenUpdating = False  'My computer is not very fast, thus I use this line of
                                        'code to save some computing power and time

    Dim internet_object As InternetExplorer
    Dim i As Integer

         For i = 3 To Sheets(1).Cells(3, 7).End(xlDown).Row
              FX_Pair = Sheets(1).Cells(i, 7)

              Set internet_object = New InternetExplorer
              internet_object.Visible = True
              internet_object.navigate "https://www.bloomberg.com/quote/" & FX_Pair & ":CUR"

              Application.Wait Now + TimeValue("00:00:05")

              internet_object.document.getElementsByClassName("class")(0).getElementsByTagName ("value__b93f12ea")  '--> DEBUGGER PROBLEM
                                                                                                                    'My goal here is to "grab" the PREV CLOSE
                                                                                                                    'value from the website
                    With ActiveSheet
                        .Range(Cells(i, 9)).Value = HTML_element.Children(0).textContent
                    End With

             Sheets(1).Range(Cells(i, 9)).Copy   'Not sure if these 2 lines are unnecesary
             ActiveSheet.Paste

         Next i

    Application.ScreenUpdating = True

End Sub

预期结果

当我在列 G:G 的单元格中输入“USDGBP”时,宏将转到 https://www.bloomberg.com/quote/EURGBP:CUR 并“获取”PREV CLOSE 值 0.8732(使用今天的值)并插入它在第一列:I

的相应行中

到目前为止,我只是面对调试器,对如何解决问题没有太多想法。

【问题讨论】:

  • 您在这里遇到了多个问题...Set internet_object = New InternetExplorer 需要在您的循环之外。 .Range(Cells(i, 9)).Value 应该只是 Cells(i, 9).Value。 `internet_object.document.getElementsByClassName` 行需要成为操作的一部分,例如 Range("A1").Value = internet_object.document.getElementsByClassName... 等。
  • 我今天没有时间看这个,但是我会看看我是否可以在明天使用.querySelector 来帮助您解决这个问题:)

标签: excel vba web-scraping


【解决方案1】:

您可以在循环中使用类选择器。模式

.previousclosingpriceonetradingdayago .value__b93f12ea

指定获取类为value__b93f12ea 的子元素,其父类为previousclosingpriceonetradingdayago。这 ”。”前面是 css class selector,这是一种更快的选择方式,因为现代浏览器针对 css 进行了优化。两个类之间的空格是descendant combinator。 querySelector 从网页 html 文档中返回此模式的第一个匹配项。

这在页面上匹配:

您可以在这里再次看到父子关系和类:

<section class="dataBox previousclosingpriceonetradingdayago numeric">
    <header class="title__49417cb9"><span>Prev Close</span></header>
    <div class="value__b93f12ea">0.8732</div>
</section>

注意如果您是彭博客户,请查看他们的APIs。此外,您很可能可以从其他专用 API 获得相同的信息,这将允许更快、更可靠的 xhr 请求。


VBA(Internet Explorer):

Option Explicit
Public Sub test()
    Dim pairs(), ws As Worksheet, i As Long, ie As Object
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set ie = CreateObject("InternetExplorer.Application")
    With ws
        pairs = Application.Transpose(.Range("G2:G" & .Cells(.rows.Count, "G").End(xlUp).Row).Value) ' assumes pairs start in row 2
    End With
    Dim results()
    ReDim results(1 To UBound(pairs))
    With ie
        .Visible = True
        For i = LBound(pairs) To UBound(pairs)
            .Navigate2 "https://www.bloomberg.com/quote/" & pairs(i) & ":CUR", False
             While .Busy Or .readyState < 4: DoEvents: Wend
             results(i) = .document.querySelector(".previousclosingpriceonetradingdayago .value__b93f12ea").innerText
        Next
        .Quit
    End With
    ws.Cells(2, "I").Resize(UBound(results), 1) = Application.Transpose(results)
End Sub

对于非常有限数量的请求(导致阻塞),您可以使用 xhr request 并正则表达式输出该值。我假设对在第一张纸中并从 G2 开始。我还假设 G 列中没有空单元格或无效对,直到包含最后一个要搜索的对。否则,您将需要开发代码来处理此问题。

试试正则表达式here

Option Explicit
Public Sub test()
    Dim re As Object, pairs(), ws As Worksheet, i As Long, s As String
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set re = CreateObject("VBScript.RegExp")
    With ws
        pairs = Application.Transpose(.Range("G2:G" & .Cells(.rows.Count, "G").End(xlUp).Row).Value) ' assumes pairs start in row 2
    End With
    Dim results()
    ReDim results(1 To UBound(pairs))
    With CreateObject("MSXML2.XMLHTTP")
        For i = LBound(pairs) To UBound(pairs)
            .Open "GET", "https://www.bloomberg.com/quote/" & pairs(i) & ":CUR", False
            .send
            s = .responseText
            results(i) = GetCloseValue(re, s, "previousClosingPriceOneTradingDayAgo%22%3A(.*?)%2")
        Next
    End With
    ws.Cells(2, "I").Resize(UBound(results), 1) = Application.Transpose(results)
End Sub
Public Function GetCloseValue(ByVal re As Object, inputString As String, ByVal pattern As String) As String 'https://regex101.com/r/OAyq30/1
    With re
        .Global = True
        .MultiLine = True
        .IgnoreCase = False
        .pattern = pattern
        If .test(inputString) Then
            GetCloseValue = .Execute(inputString)(0).SubMatches(0)
        Else
            GetCloseValue = "Not found"
        End If
    End With
End Function

【讨论】:

  • 非常感谢您的意见@QHarr。该代码本身运行良好,但不幸的是,我有另一个“私人子”,每次某个列中的值发生变化时都会注册一个时间戳。基本上,后者与“application.undo”一起使用,以撤消最近的更改以将其与新更改进行比较。当我启动你的代码时,调试器会提示我这个“application.undo”,我没有在网上找到太多信息。你知道为什么两个宏会发生冲突吗?
  • 嗨@QHarr。抱歉,我完全忘了循环你:我按照你的建议创建了一个新帖子 --> link。我现在所做的只是将不同工作簿中的两个 Subs 分开,然后我手动复制并粘贴结果以“桥接”数据。我尝试使用“debug.print [variable;在本例中为“results”]”输出数据——我认为这将是最好的选择之一——但我发现类型不匹配。
【解决方案2】:

试试下面的代码: 但在确保通过转到工具> 参考 > 添加 2 个参考之前,然后查找 Microsoft HTML 对象库和 Microsoft Internet 控件

此代码适用于您的示例。

Sub getPrevCloseValue()

Dim ie As Object

Dim mySh As Worksheet
Set mySh = ThisWorkbook.Sheets("Sheet1")

Dim colG_Value As String
Dim prev_value As String


For a = 3 To mySh.Range("G" & Rows.Count).End(xlUp).Row
    colG_Value = mySh.Range("G" & a).Value

    Set ie = CreateObject("InternetExplorer.Application")
    ie.Visible = True
    ie.navigate "https://www.bloomberg.com/quote/" & colG_Value & ":CUR"
    Do While ie.Busy: DoEvents: Loop
    Do Until ie.readyState = 4: DoEvents: Loop
    'Application.Wait (Now + TimeValue("00:00:03")) 'activate if having problem with delay

    For Each sect In ie.document.getElementsByTagName("section")
        If sect.className = "dataBox previousclosingpriceonetradingdayago numeric" Then
            prev_value = sect.getElementsByTagName("div")(0).innerText
            mySh.Range("I" & a).Value = prev_value
            Exit For
        End If
    Next sect

Next a

我有一个使用 vba 进行基本网络自动化的视频教程,其中包括网络数据抓取和其他命令,请查看以下链接: https://www.youtube.com/watch?v=jejwXID4OH4&t=700s

【讨论】:

    猜你喜欢
    • 2015-01-19
    • 2021-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多