Невозможно проанализировать конкретное значение элемента с веб-страницы с помощью vba

Я создал сценарий на VBA для получения определенного элемента с веб-страницы. Значение интересующего меня элемента (Year Built) не всегда находится в одном и том же индексе, поэтому использование индекса здесь - неправильная идея. Я даю две ссылки ниже только потому, что значения элементов указаны в разных индексах на двух веб-страницах.

первый сайт

второй сайт

Мой первоначальный подход к получению значения был следующим:

.NextSibling.getElementsByTagName("td")(3).innerText

Значение, которое я ищу, отображается как:

введите описание изображения здесь

То, что я пытаюсь сделать сейчас (работает, но позиция все еще гипотетическая и сломается, если позиция изменится):

.NextSibling.LastChild.PreviousSibling.innerText

Я уже создал:

Sub GetInformation()
    Dim Http As New XMLHTTP60, links, i&
    Dim Htmldoc As New HTMLDocument, link
    Dim Wb As Workbook, ws As Worksheet, r&

    Set Wb = ThisWorkbook
    Set ws = Wb.Worksheets("Sheet1")

    links = Array( _
        "https://esearch.brazoscad.org/Property/View/114414", _
        "https://esearch.brazoscad.org/Property/View/117608" _
       )

    For Each link In links
        With Http
            .Open "GET", link, False
            .send
            Htmldoc.body.innerHTML = .responseText
        End With


        With Htmldoc.querySelectorAll("tr")
            For i = 0 To .Length - 1
                If InStr(.item(i).innerText, "Year Built") > 0 Then
                    r = r + 1: ws.Cells(r, 1) = .item(i).NextSibling.LastChild.PreviousSibling.innerText
                End If
            Next i
        End With
    Next link
End Sub

Как я могу получить конкретную ценность элемента с веб-страницы?

Кстати, если .querySelector() поддерживает :nth-of-type(), что не так с .querySelector("table:nth-of-type(2) tr"), когда я использую его в скрипте, который не работает.


person MITHU    schedule 09.11.2019    source источник


Ответы (1)


если .querySelector () поддерживает: nth-of-type (), что не так с .querySelector ("table: nth-of-type (2) tr"), когда я использую его в скрипте, который не работает

Он поддерживается при использовании Microsoft Internet Controls для автоматизации браузера (IE8 +) и создается HTMLDocument с ie.Document. Затем у вас будет доступ к очень небольшому количеству селекторов псевдоклассов < / а>. Это не относится к HTMLDocument, когда innerHTML предоставляется через MSXML2.XMLHTTP. Помните, что контент, который вы вводите в свою HTMLDocument переменную .innerHTML, будет отличаться в XHR, где javascript не будет работать, и IE, где будут выполняться js, и браузер изменит контент / запросит дополнительные файлы, оставив вам измененный .document. Как упоминалось в начале, для последнего, конечно, также существует зависимость режима браузера / документа.

Селектор table:nth-of-type(2) tr, даже если он поддерживается, здесь не подходит.

Значение интересующего меня элемента (Год постройки) не всегда находится в одном и том же индексе, поэтому использование индекса здесь - неправильная идея.

Основываясь на более тщательном изучении вашего кода, кажется, что изменчивость, которую вы пытаетесь учесть, - это потенциальная разница в количестве столбцов в целевой таблице и, следовательно, возможность для вашего элемента находиться в td с другим индексом в данной строке (например, вы не пытаетесь учитывать изменчивость строк ...). Итак, мы ищем в целом другие отношения; связь между элементами не требуется; или динамически определять соответствующий индекс; или, может быть, даже их комбинация.

Соображения ИМО:

  • Тот же URI, но альтернативный элемент на странице с более коротким, надеюсь, более надежным селектором;
  • Другой URI XHR, где желаемый элемент связан с более надежным селектором, например. уникальный идентификатор;
  • Тег script с красивой строкой для захвата регулярного выражения (var yearBuilt = 1234;);
  • Позиционная стратегия, которая имеет меньше зависимостей и / или, исходя из опыта, более высокую вероятность стабильности во времени.

Кроме того,

  • Оптимизирован для более быстрого поиска

Я понимаю, что вышесказанное является повторным хешированием той же общей идеи.

Глядя на соображения и две предоставленные ссылки:

Год постройки, связанный с MAIN AREA, присутствует только в одном месте в документе. Примечание: я сохраняю предположение, что это следующая строка после соответствующей строки заголовка. Я не изучил достаточно ссылок, чтобы узнать, может ли стоимость в этом году варьироваться в зависимости от площади собственности, и вы не указали, что является обязательным. В этом примере MAIN AREA отображается как первая часть с датой сборки.

Страница, похоже, не извлекает необходимый контент из дополнительных запросов, поэтому альтернативный источник не сразу очевиден. Похоже, что нет специального общедоступного API. функция поиска не предоставляет необходимую информацию из своих запросов POST, а загружаемые файлы имеют задержку на 3-4 месяца, в основном это .txt и не предлагают никаких реальных возможностей для более быстрое определение необходимой информации (на самом деле было бы намного труднее и менее надежно).

Остается рассмотреть вопрос 4. Вам нужен способ найти правый столбец в правой таблице. HTML имеет очень повторяющуюся структуру с несколькими хорошими «зацепками». Вместо того, чтобы создавать более хрупкий путь, зависящий от взаимосвязей таблиц, вы разумно выбрали цикл по trs (следовательно, должен быть в таблице), ища строку заголовка ключа в tr innerText. Таким образом, риск появления строки заголовка в другом столбце и / или другой таблице был снижен в пользу более короткого пути обхода и гибкости для перехода к следующей строке, которая, как предполагается, содержит интересующие данные.

Пока что я считаю хороший выбор, хотя лично я бы предпочел ограничить поиск заголовками (th), а затем перейти к родительскому. Дополнительным преимуществом здесь является то, что я мог бы смягчить вашу следующую часть:

.Item(i).NextSibling.LastChild.PreviousSibling.innerText

Здесь вы создали ненужное предположение / риск, что интересующая вас колонка всегда будет предпоследней. Хотя вы могли бы зациклить все заголовки и перейти к родительскому узлу, я бы рискнул ограничиться соответствующей таблицей, ища уникальную строку в заголовке панели, а затем возьму таблицу next-sibling перед исследованием заголовков. Он вводит ИМО разумное предположение относительно отношения panel heading к table и panel содержания. Затем это позволяет нам найти правильный индекс для заголовка на основе table и использовать этот индекс для индексации в tds следующей строки. Это смягчает положение, не являющееся предпоследним. Затем вы можете поискать дальнейшие оптимизации. Я пошел с установкой совпадений в переменные для более быстрого обращения.

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

В целом ваша стратегия хороша. Я лично рискнул бы получить правильную таблицу, если бы предположил, что правый столбец является предпоследним. Я пошел с немного другими отношениями и динамически определил правильный индекс. Я не совсем доволен решением, но мне кажется, что оно достаточно хорошее.


VBA:

Option Explicit

Public Sub GetInformation()
    Dim Http As New XMLHTTP60, links, i&
    Dim htmlDoc As New HTMLDocument, link
    Dim Wb As Workbook, ws As Worksheet, r&

    Set Wb = ThisWorkbook
    Set ws = Wb.Worksheets("Sheet1")

    links = Array( _
            "https://esearch.brazoscad.org/Property/View/114414", _
            "https://esearch.brazoscad.org/Property/View/117608" _
            )

    For Each link In links
        With Http
            .Open "GET", link, False
            .send
            htmlDoc.body.innerHTML = .responseText
        End With

        Dim panels As Object, table As Object, headers As Object

        Set panels = htmlDoc.querySelectorAll(".panel-heading")

        For i = 0 To panels.Length - 1
            If InStr(panels.Item(i).innerText, "Property Improvement - Building") > 0 Then
                Set table = panels.Item(i).NextSibling 'assumption on relationship
                Exit For
            End If
        Next i

        If Not table Is Nothing Then

            Set headers = table.getElementsByTagName("th")

            For i = 0 To headers.Length - 1
                If InStr(headers(i).innerText, "Year Built") > 0 Then
                    r = r + 1: ws.Cells(r, 1) = headers(i).ParentNode.NextSibling.Children(i).innerText
                    Exit For
                End If
            Next
        End If
        Set htmlDoc = Nothing: Set table = Nothing
    Next link
End Sub

Ссылки (VBE> Инструменты> Ссылки):

  1. Библиотека объектов Microsoft HTML
  2. Microsoft XML v (n) 'ваша версия
person QHarr    schedule 09.11.2019
comment
Я немного потерял свой английский, но думаю, что смысл понятен. Исходная стратегия хороша, и она просто становится компромиссом. - person QHarr; 09.11.2019
comment
Упс ... хорошее место ... Сначала я написал другую стратегию и забыл удалить эту ссылку! - person QHarr; 09.11.2019