VBA Web Scrape Получить элементы по имени класса Ошибка несоответствия типов - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь получить некоторые данные из статистики НХЛ в Excel с помощью VBA, используя следующий код, но получаю ошибку несоответствия типов.Есть идеи?

Код:

Private Sub Hawks()

    Dim IE As New InternetExplorer
    Dim element As HTMLAnchorElement
    Dim elements As HTMLElementCollection

    IE.Visible = False
    IE.navigate "https://www.nhl.com/blackhawks/stats"

    Do
    DoEvents
    Loop Until IE.readyState = READYSTATE_COMPLETE

    Dim Doc As HTMLDocument
    Set Doc = IE.document
    Set elements = Doc.getElementsByClassName("name-col__firstName")

    Dim count As Long

    Dim erow As Long
    count = 0

    For Each element In sDD
    If element.className = "name-col__firstName" Then
    erow = Sheet1.Cells(Rows.count, 1).edn(xlUp).Offset(1, 0).Row
    Cells(erow, 1) = HTML.getElementsByTagName("span")(count).innerText
    count = count + 1
    End If
    Next element

End Sub

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Ваш код:

Возможно, вы объединили отдельные биты кода, но у вас непоследовательное использование переменных.sDD Я думаю, должно быть elements, HTML должно быть Doc.Связанные объявления типов переменных для элемента и элементов должны быть следующими:

Dim element As IHTMLSpanElement
Dim elements As IHTMLElementCollection

Если элементы представляют собой коллекцию с одинаковым именем класса, тогда вам не нужно:

If element.className = "name-col__firstName" 

40 соответствующих элементов для этого класса на странице, некоторые из которых повторяют ту же информацию, которую вы не ограничивали отдельной таблицей.

Вы пытаетесь проиндексировать коллекцию тегов span, используя тот же индекс, что и в коллекции имен классов, но на самом деле коллекция span имеет длину 1354 элемента, и индексы на странице не соответствуют.

Вы хотите настроить таргетинг только на интересующую таблицу и элементы в ней.Я покажу вам, как позже.

У вас также есть опечатка в этой строке:

erow = Sheet1.Cells(Rows.count, 1).edn(xlUp).Offset(1, 0).Row

Это должно быть End(xlUp).


Просто имена:

Если вы используете только информацию об имени, я бы использовал наследующий CSS-комбинатор , чтобы нацелить таблицу по идентификатору родительского элемента div, а затем фактические имена по значению ихатрибут класса.Это быстрый метод с небольшой дальнейшей оптимизацией кода путем отключения обновления экрана.

Все имена находятся в таблице с идентификатором skater-table.Селектор CSS для этого #skater-table.# обозначает идентификатор.Сами имена в этом элементе id родительской таблицы имеют атрибут class, который содержит строковое значение text.Это записывается как селектор атрибута CSS = значение [class*=text].Символ * означает, что значение имени класса содержит значение text.

. Пример соответствующих элементов можно посмотреть здесь:

image


VBA: Полный список имен.

Option Explicit
Public Sub GetHawksNamesInfo()
    Dim IE As InternetExplorer, playerList As Object, player As Long
    Application.ScreenUpdating = False
    Set IE = New InternetExplorer
    With IE
        .Visible = False
        .navigate "https://www.nhl.com/blackhawks/stats"
        While .Busy Or .readyState < 4: DoEvents: Wend

        Set playerList = IE.document.querySelectorAll("#skater-table [class*=text]")

        With ThisWorkbook.Worksheets("Sheet1")
            For player = 0 To playerList.Length - 1
                .Cells(player + 1, 1) = playerList.item(player).innerText
            Next
        End With
        .Quit
    End With
    Application.ScreenUpdating = True
End Sub

Весь стол:

Вы можете взять всю таблицу вместе с фотографиями игроков, скопировав вбуфер обмена, а затем вставьте его на лист, используя:

Option Explicit
Public Sub GetInfo()
    Dim IE As InternetExplorer, clipboard As Object
    Application.ScreenUpdating = False
    Set IE = New InternetExplorer
    With IE
        .Visible = False
        .navigate "https://www.nhl.com/blackhawks/stats"
        While .Busy Or .readyState < 4: DoEvents: Wend

        Set clipboard = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
        clipboard.SetText .document.querySelector("#skater-table table").outerHTML
        clipboard.PutInClipboard
        ThisWorkbook.Worksheets("Sheet1").Cells(1, 1).PasteSpecial

        .Quit
    End With
    Application.ScreenUpdating = True
End Sub

API:

Настоящим золотым прииском для ботаников-статистиков является API.При проверке HTML-кода для страницы я обнаружил этот сценарий, в котором подробно описаны значения на стороне клиента, доступные с помощью API .Вероятно, поэтому был выполнен вызов API на основе queryString.С точки зрения непрофессионала, набор значений, которые вы можете объединить в строку, которую вы отправляете в веб-интерфейс, предоставляет ответ, содержащий все данные статистики, в данном случае в формате JSON .API-интерфейсы, как правило, являются отличным способом получения данных для клиентов и более надежны, чем очистка веб-страниц.

image

Я решил отслеживать веб-трафик, чтобы узнать, был ли выполнен вызов API, которыйЯ мог бы схватить.Bazinga!Был выполнен следующий вызов API на основе queryString, который возвращает ответ JSON.

https://statsapi.web.nhl.com/api/v1/teams/16?hydrate=franchise(roster(season=20182019,person(name,stats(splits=[yearByYear]))))

Примечание. Если вставить вышеуказанную строку в браузер FireFox и нажать клавишу ввода, вы можете просмотреть ответ JSON.

Прокручивая вниз в FireFox, вы можете найти Джерси № 19, например, и просмотреть их информацию:

image


Это предоставляет огромное количество статистической информации, возвращаемой в виде строки JSON.Вот лишь краткий обзор того, что содержится внутри (это даже не вся информация для одного показанного игрока!):

image

вызов API XMLHTTP и JSONпарсинг:

Вы можете вообще не открывать браузер и выдавать действительно быстрый XMLHTTP-запрос на API и получать всю эту информацию в ответе JSON, с которым вы можете работать, используя JSONParser .

В JSON слишком много информации, чтобы показать вам, как ее анализировать.Вот только пример синтаксического анализа всех имен из ответа (обратите внимание, что это полный список сезона).После загрузки и импорта JSONConverter.bas по указанной ссылке вам нужно перейти на VBE> Инструменты> Ссылки> Добавить ссылку на Microsoft Scripting Runtime.

Option Explicit
Public Sub GetInfo()
    Dim strJSON As String, json As Object
    Const URL  As String = "https://statsapi.web.nhl.com/api/v1/teams/16?hydrate=franchise(roster(season=20182019,person(name,stats(splits=[yearByYear]))))"

    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", URL, False
        .setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
        .send
        strJSON = .responseText
    End With
    Set json = JsonConverter.ParseJson(strJSON)("teams")(1)("franchise")("roster")("roster")
    Dim player As Object
    For Each player In json
        Debug.Print player("person")("fullName")
    Next
End Sub

Редактировать: В некоторых случаях, похоже, возникают проблемы со ссылкой на буфер обмена с поздним связыванием.Вот общий метод раннего связывания, где hTable является целевым объектом HTMLTable.

Для раннего связывания буфера обмена перейдите в VBE> Инструменты> Ссылки> Библиотека объектов Microsoft-Forms 2.0.

Если вы добавляете пользовательскую форму вваш проект, библиотека будет автоматически добавлена.

Dim clipboard As DataObject
Set clipboard = New DataObject
clipboard.SetText hTable.outerHTML
clipboard.PutInClipboard
ThisWorkbook.Worksheets("Sheet1").Cells(1, 1).PasteSpecial
0 голосов
/ 09 октября 2018

Этот метод зацикливает строки вашей таблицы, а не коллекцию классов name-col__firstName.

Я проверил это, и похоже, что оно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...