嗨伙伴,我是一个正在研究如何编写程序来帮助交易选项的初学者。现在的问题是,当我尝试从网站上的表中删除一些数据时,我无法识别代码以找出我需要的信息。附加两张图片后,您可以看到网站上有两个表具有相同的表类名,但有不同的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
我该如何改进我的代码?我很高兴听到你的建议。
您可以使用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(管理页面样式)进行了优化。我们通过querySelector
和querySelectorAll
方法应用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中添加“'”以保留格式:
ws.Cells(r, c) = IIf(c Mod 4 = 0, "'" & td.innerText, td.innerText)