vba网络抓取。无法访问表中的数据

问题描述 投票:2回答:1

表1:Table1

表2:Table2

嗨伙伴,我是一个正在研究如何编写程序来帮助交易选项的初学者。现在的问题是,当我尝试从网站上的表中删除一些数据时,我无法识别代码以找出我需要的信息。附加两张图片后,您可以看到网站上有两个表具有相同的表类名,但有不同的ID。我需要做的就是从表中提取ID为“option”的数据。以下是我的代码:

Sub hsiotpions()
    Dim ieObj As InternetExplorer
    Dim htmlEle As IHTMLElement
    Dim i As Integer

    i = 1

    Set ieObj = New InternetExplorer
    ieObj.Visible = False
    ieObj.navigate "https://www.hkex.com.hk/Market-Data/Futures-and-Options-Prices/Equity-Index/Hang-Seng-Index-Futures-and-Options?sc_lang=en#&product=HSI"

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

    For Each htmlEle In ieObj.document.getElementById("option").getElementByTagName("tr")
        With ActiveSheet
            .Range("A" & i).Value = htmlEle.Children(0).textContent
            .Range("B" & i).Value = htmlEle.Children(1).textContent
            .Range("C" & i).Value = htmlEle.Children(2).textContent
            .Range("D" & i).Value = htmlEle.Children(3).textContent
            .Range("E" & i).Value = htmlEle.Children(4).textContent
            .Range("F" & i).Value = htmlEle.Children(5).textContent
            .Range("G" & i).Value = htmlEle.Children(6).textContent
            .Range("H" & i).Value = htmlEle.Children(7).textContent
            .Range("I" & i).Value = htmlEle.Children(8).textContent
            .Range("J" & i).Value = htmlEle.Children(9).textContent
            .Range("K" & i).Value = htmlEle.Children(10).textContent
            .Range("L" & i).Value = htmlEle.Children(11).textContent
        End With

        i = i + 1

    Next htmlEle

End Sub

我该如何改进我的代码?我很高兴听到你的建议。

excel vba web-scraping options trading
1个回答
0
投票

您可以使用id来选择表。只需确保正确处理第4列和第8列以便写入工作表。

在您的代码中,如果您不使用复数,则会出现错误:

getElementByTagName("tr")

那应该是

getElementsByTagName("tr")

哪个返回一个集合。

表体的行实际上具有相同的类名,我使用下面的行来循环行。

我不喜欢硬编码等待时间,但因为我目前无法看到页面满载时的良好指示,您可以调整以下行直到获得所有行

Application.Wait Now + TimeSerial(0, 0, 10)

结束参数是以秒为单位的等待时间。

Option Explicit    
Public Sub GetTable()
    Dim ws As Worksheet, ie As Object, table As Object, headers()
    Dim headersTop As Object, ele As Object

    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set ie = CreateObject("InternetExplorer.Application")
    headers = Array("OI", "Volume", "IV", "Bid/Ask", "Last", "Strike", "Last", "Bid/Ask", "IV", "Volume", "OI")  '<== This is second row of headers

    With ie
        .Visible = True
        .Navigate2 "https://www.hkex.com.hk/Market-Data/Futures-and-Options-Prices/Equity-Index/Hang-Seng-Index-Futures-and-Options?sc_lang=en#&product=HSI"

        While .Busy Or .readyState < 4: DoEvents: Wend

        Application.Wait Now + TimeSerial(0, 0, 10)

        Set table = .document.querySelector("#option")
        Set headersTop = .document.querySelectorAll("#option tr:first-child th")  '<== This is top row of headers which involves merged table cells. I prepare the excel sheet in the same way in the code below.
        ws.Range("A1:D1").Merge
        ws.Range("A1").Value = headersTop.item(0).innerText  ' CALL
        ws.Range("E1:G1").Merge
        ws.Range("E1") = headersTop.item(1).innerText  '< Date
        ws.Range("H1:K1").Merge
        ws.Range("H1") = headersTop.item(2).innerText  '< PUT
        ws.Cells(2, 1).Resize(1, UBound(headers) + 1) = headers
        Dim r As Long, c As Long, td As Object, tr As Object
        r = 3
        For Each tr In table.getElementsByClassName("tdrow") 'loop the rows below the headers by using class name to isolate
            c = 1
            For Each td In tr.getElementsByTagName("td") '< loop table cells i.e. columns of rows
                ws.Cells(r, c) = IIf(c Mod 4 = 0, "'" & td.innerText, td.innerText)  '< If column number is 4 or 8 then add "'" in front so formatting preserved
            c = c + 1
            Next
            r = r + 1
        Next
        .Quit
    End With
End Sub

CSS选择器:

我最初使用的是css selectors。现代浏览器针对使用css(管理页面样式)进行了优化。我们通过querySelectorquerySelectorAll方法应用css选择器,在本例中为ie.document

以下是#option tr:first-child th正在选择的内容:

你可以看到它是顶级标题。由于表中实际上有更多的列,我们知道我们已经合并了单元格。

由于我们需要多个标题,我们使用querySelectorAll方法,而不是返回单个元素的querySelector,以返回由(“”)内的选择器指定的匹配元素的nodeList。然后,通过从0开始的索引访问nodeList中的项。


表体:

我已经通过id检索了感兴趣的表

Set table = .document.querySelector("#option")

#是一个css id选择器,在语义上等同于:

Set table = .document.getElementById("option")

只是,css选择器方法应该证明更快,除了非常老的IE版本。

当我们有表时,我们知道行是tr元素,td是表格单元格(列)。

查看我们的table变量中的表行,您会看到我们有两个不同长度的标题:

这就是为什么我选择不循环这些尝试写出来的原因。底行的值是静态的,所以我把它们放在数组变量headers中写出来。顶行具有日期的动态值,因此我使用css选择器自行检索该行:

Set headersTop = .document.querySelectorAll("#option tr:first-child th")

并且索引到返回的nodeList中以将项目放置在由一个网页布局指示的各个合并的单元格中。

但请注意,表体行都具有相同的类名:

所以,我通过在表行上使用类名来选择这些:

For Each tr In table.getElementsByClassName("tdrow")

然后我在这些行中循环列:

For Each td In tr.getElementsByTagName("td")

因为第4列和第8列具有Excel将尝试处理的格式,因此您希望在fron中添加“'”以保留格式:

enter image description here

ws.Cells(r, c) = IIf(c Mod 4 = 0, "'" & td.innerText, td.innerText)
© www.soinside.com 2019 - 2024. All rights reserved.