vba веб-слом.Невозможно получить доступ к данным из таблицы - PullRequest
0 голосов
/ 25 февраля 2019

Table1: Table1

Table2: Table2

Привет, друзья, я новичок, который изучает, как кодироватьпрограммы, помогающие торговать опционами.Проблема сейчас в том, что, когда я пытаюсь удалить некоторые данные из таблицы на веб-сайте, я не могу определить код, чтобы выяснить, какая информация мне нужна.Прикрепив две картинки, вы можете видеть, что на сайте есть две таблицы с одинаковым именем класса таблицы, но разными идентификаторами.Все, что мне нужно сделать, это извлечь данные из таблицы с идентификатором «опция».Ниже приведены мои коды:

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

Как я могу улучшить свои коды?Я рад услышать ваш совет.

1 Ответ

0 голосов
/ 25 февраля 2019

Вы можете использовать идентификатор для выбора таблицы.Просто убедитесь, что вы правильно обрабатываете столбцы 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 выбирает:

image

Вы можете видеть, что это заголовки верхнего уровня.Поскольку в таблице фактически больше столбцов, чем это, мы знаем, что мы объединили ячейки.

Поскольку нам нужно более одного заголовка, мы используем метод querySelectorAll, в отличие от querySelector, который возвращает один элемент, чтобы вернуть nodeList соответствующих элементов, как указано селектором внутри ("«).Доступ к элементам в списке узлов затем осуществляется по индексу, начинающемуся с 0.


Тело таблицы:

Я получил интересующую таблицу по id с

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

# Является селектором идентификатора CSS и семантически эквивалентен:

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

Просто метод селектора CSS должен работать быстрее, за исключением очень старых версий IE.

Когда у нас есть таблица, мы знаемчто строки - это элементы tr, а td - ячейки таблицы (столбцы).

Глядя на строки таблицы в нашей переменной table, вы видите, что у нас есть два заголовка различной длины:

image

Вот почему я решил не зацикливать эти попытки выписать.Нижний ряд является статическим в значениях, поэтому я помещаю их в переменную массива headers для записи.Верхняя строка имеет динамическое значение для даты, поэтому я использую селектор css, чтобы извлечь эту строку самостоятельно:

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

и индексировать в возвращенный список узлов, чтобы поместить элементы в различные объединенные ячейки, как продиктованоодин макет веб-страницы.

Но обратите внимание, что все строки тела таблицы имеют одинаковое имя класса:

image

Итак, я выбираю их, используя имя класса настроки таблицы с:

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

Затем я зацикливаю столбцы внутри этих строк с помощью:

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

Поскольку столбцы 4 и 8 имеют формат, Excel будет пытаться обрабатывать их как даты / дробиВы хотите добавить «» во фронт для сохранения форматирования:

enter image description here

ws.Cells(r, c) = IIf(c Mod 4 = 0, "'" & td.innerText, td.innerText)
...