Переменная объекта метода VBA GetElementsById не установлена

Я пытаюсь выбрать идентификатор главного меню этой страницы http://greyhoundstats.co.uk/index.php с пометкой ("menu_wholesome"), чтобы позже получить их гиперссылки. В документе HTML есть два тега с этим идентификатором, <div> и его дочерний элемент <ul>, но когда я ищу их с помощью приведенного ниже кода, я получаю сообщение об ошибке «объектная переменная не установлена».

Option Explicit

Public Const MenuPage As String = "http://greyhoundstats.co.uk/index.php"

Sub BrowseMenus()

Dim XMLHTTPReq As New MSXML2.XMLHTTP60
Dim HTMLDoc As New MSHTML.HTMLDocument

Dim MainMenuList As MSHTML.IHTMLElement
Dim aElement As MSHTML.IHTMLElementCollection
Dim ulElement As MSHTML.IHTMLUListElement
Dim liElement As MSHTML.IHTMLLIElement

XMLHTTPReq.Open "GET", MenuPage, False
XMLHTTPReq.send

HTMLDoc.body.innerText = XMLHTTPReq.responseText

    Set MainMenuList = HTMLDoc.getElementById("menu_wholesome")(0) '<-- error happens here

End Sub

Кто-нибудь знает, почему getElementsById не может найти указанный идентификатор, хотя он является частью набора HTML-документов? Я знаю, что этот метод должен возвращать уникальный идентификатор, но когда у нас есть один и тот же идентификатор, на который ссылаются другие теги, я также знаю, что верну первый найденный идентификатор, который должен быть <div id="menu_wholesome"> частью запрашиваемой HTML-страницы.


person thiggy01    schedule 28.09.2018    source источник


Ответы (2)


Во-первых: вы хотите работать и устанавливать innerHTML, поскольку вы собираетесь перемещаться по документу DOM.

Во-вторых: эта линия

Set MainMenuList = HTMLDoc.getElementById("menu_wholesome")(0)

Это неправильно. getElementById возвращает один элемент, который вы не можете индексировать. Вы индексируете коллекцию.

Обратите внимание: и div, и ul ведут к одному и тому же содержимому.

Если вы хотите выбрать их отдельно, используйте querySelector

HTMLDoc.querySelector("div#menu_wholesome")
HTMLDoc.querySelector("ul#menu_wholesome")

Вышеуказанная цель сначала по имени тега, а затем по атрибуту id.

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

HTMLDoc.querySelectorAll("#menu_wholesome")

Затем вы можете индексировать в nodeList, например.

HTMLDoc.querySelectorAll("#menu_wholesome").item(0)

VBA:

Option Explicit

Public Const MenuPage As String = "http://greyhoundstats.co.uk/index.php"
Sub BrowseMenus()
    Dim sResponse As String, HTMLDoc As New MSHTML.HTMLDocument
    Dim MainMenuList As Object, div As Object, ul As Object

    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", MenuPage, False
        .setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
        .send
        sResponse = StrConv(.responseBody, vbUnicode)
    End With

    sResponse = Mid$(sResponse, InStr(1, sResponse, "<!DOCTYPE "))
    HTMLDoc.body.innerHTML = sResponse

    Set MainMenuList = HTMLDoc.querySelectorAll("#menu_wholesome")

    Debug.Print MainMenuList.Length

    Set div = HTMLDoc.querySelector("div#menu_wholesome")
    Set ul = HTMLDoc.querySelector("ul#menu_wholesome")

    Debug.Print div.outerHTML
    Debug.Print ul.outerHTML

End Sub
person QHarr    schedule 28.09.2018
comment
Как всегда, идеальный класс по этому вопросу. У тебя дар объяснять новичкам вроде меня. Это ясно теперь. Большое спасибо за добрый ответ. - person thiggy01; 28.09.2018
comment
Не за что. Рад помочь. Идентификаторы должны быть уникальными, но я часто обнаруживаю, что это не так, а затем querySelectorAll — это способ собрать их в список узлов. - person QHarr; 28.09.2018
comment
Метод QuerySelector — это именно то, что я искал. - person thiggy01; 28.09.2018
comment
Что-то пропущено в ответе, пожалуйста? Приведенный выше метод - это в основном то, что вы сейчас указали в своем ответе, за исключением того, что вы не обеспечиваете возможное кодирование контента или возможное извлечение результатов из кеша. - person QHarr; 28.09.2018
comment
Мне очень понравился ваш ответ, но ошибка объекта не установлена ​​была вызвана .innerText вместо .innerHTML. Я выбираю его в качестве основного ответа. Спасибо - person thiggy01; 29.09.2018
comment
Привет, это именно то, что я сказал в своей первой строке: вы хотите работать и устанавливать innerHTML, поскольку вы собираетесь пройти документ DOM. Это то, что я делаю с HTMLDoc.body.innerHTML = sResponse, за исключением того, что я сначала гарантирую, что responseText декодируется в качестве дополнительной меры безопасности. - person QHarr; 29.09.2018
comment
О, теперь я вижу. Я не понял, что делает ваш код. Извините за мое невежество. - person thiggy01; 29.09.2018
comment
Возможно, я должен был быть более явным. Если у вас есть какие-либо вопросы о моем коде, пожалуйста, не стесняйтесь спрашивать. - person QHarr; 29.09.2018
comment
Я вижу, что вы используете .responseBody, преобразованный в юникод, а не .responseText, который я использую, но я не понимаю, почему, и я также не понимаю, почему вы использовали mid и instr, чтобы получить позицию ‹!DOCTYPE - person thiggy01; 29.09.2018
comment
Свойство body в ответе XMLHTTPRequest — это всегда удобный проанализированный js-объект. Это свойство может быть полезно, когда тело ответа является двоичным или текстовым. Вам не нужно использовать часть Instr. Это просто привычка с моей стороны. - person QHarr; 29.09.2018

Непонятно, чего вы пытаетесь добиться. Я только что исправил текущую проблему, с которой вы столкнулись в данный момент. .getElementById() имеет дело с отдельным элементом, поэтому, когда вы рассматриваете его как набор элементов, он выдает эту ошибку. Если вы обратите внимание на эту часть getElementBy и getElementsBy, вы увидите разницу в том, какой из них является набором элементы (не забывайте о s). Вы можете использовать (0) или что-то подобное только при использовании getElementsBy.

Вы должны сделать правильный отступ в своем коде, чтобы другие могли без проблем его прочитать:

Sub BrowseMenus()
    Const MenuPage$ = "http://greyhoundstats.co.uk/index.php"
    Dim HTTPReq As New XMLHTTP60, HTMLDoc As New HTMLDocument
    Dim MainMenuList As Object

    With HTTPReq
        .Open "GET", MenuPage, False
        .send
        HTMLDoc.body.innerHTML = .responseText
    End With

    Set MainMenuList = HTMLDoc.getElementById("menu_wholesome")
End Sub
person SIM    schedule 28.09.2018
comment
Приятно видеть тебя снова - person QHarr; 28.09.2018
comment
Спасибо @Topto. Я следую вашим рекомендациям по отступам. - person thiggy01; 28.09.2018