Вы можете использовать идентификатор для выбора таблицы.Просто убедитесь, что вы правильно обрабатываете столбцы 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 .Современные браузеры оптимизированы для работы с CSS (которые управляют стилем страницы).Мы применяем css-селекторы с помощью querySelector
и querySelectorAll
методов, в данном случае ie.document
.
Вот что #option tr:first-child th
выбирает:
Вы можете видеть, что это заголовки верхнего уровня.Поскольку в таблице фактически больше столбцов, чем это, мы знаем, что мы объединили ячейки.
Поскольку нам нужно более одного заголовка, мы используем метод querySelectorAll
, в отличие от querySelector
, который возвращает один элемент, чтобы вернуть nodeList
соответствующих элементов, как указано селектором внутри ("«).Доступ к элементам в списке узлов затем осуществляется по индексу, начинающемуся с 0.
Тело таблицы:
Я получил интересующую таблицу по id с
Set table = .document.querySelector("#option")
# Является селектором идентификатора CSS и семантически эквивалентен:
Set table = .document.getElementById("option")
Просто метод селектора CSS должен работать быстрее, за исключением очень старых версий IE.
Когда у нас есть таблица, мы знаемчто строки - это элементы tr
, а td
- ячейки таблицы (столбцы).
Глядя на строки таблицы в нашей переменной table
, вы видите, что у нас есть два заголовка различной длины:
Вот почему я решил не зацикливать эти попытки выписать.Нижний ряд является статическим в значениях, поэтому я помещаю их в переменную массива headers
для записи.Верхняя строка имеет динамическое значение для даты, поэтому я использую селектор css, чтобы извлечь эту строку самостоятельно:
Set headersTop = .document.querySelectorAll("#option tr:first-child th")
и индексировать в возвращенный список узлов, чтобы поместить элементы в различные объединенные ячейки, как продиктованоодин макет веб-страницы.
Но обратите внимание, что все строки тела таблицы имеют одинаковое имя класса:
Итак, я выбираю их, используя имя класса настроки таблицы с:
For Each tr In table.getElementsByClassName("tdrow")
Затем я зацикливаю столбцы внутри этих строк с помощью:
For Each td In tr.getElementsByTagName("td")
Поскольку столбцы 4 и 8 имеют формат, Excel будет пытаться обрабатывать их как даты / дробиВы хотите добавить «» во фронт для сохранения форматирования:
ws.Cells(r, c) = IIf(c Mod 4 = 0, "'" & td.innerText, td.innerText)