Как выбрать элемент, который не имеет Specifi c Имя класса, используя селектор запросов? - PullRequest
0 голосов
/ 18 января 2020

Я выполняю веб-просмотр VBA для отслеживания статуса отправки FedEx.

URL-адрес
https://www.fedex.com/apps/fedextrack/index.html?tracknumbers=475762806100&cntry_code=in

Текущий статус этой отправки поставляется. Я хочу извлечь это.

Я знаю, как выбирать элементы, которые имеют имя класса.

Код HTML сайта выше не имеет имен классов.

Как выбрать элемент, который не имеет заданного c имени класса, с помощью queryselector?

Я использую самый быстрый метод MSXML2.XMLHTTP в моем коде VBA. Одним из недостатков этого метода является то, что getelementbyclassname не будет работать в этом методе или любом другом способе заставить программу распознавать имя getelementbyclassname.

По этой причине я решил использовать queryselector.

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

enter image description here

Из кодов HTML Как получить внутренний текст класса с именем ----- "redesignStatusChevronTV C tank-results-item__data-label-large tank-text-center-statusChevron_key_status"?

<h3 class="redesignStatusChevronTVC tank-results-item__data-label-large tank-text-center statusChevron_key_status">Delivered</h3>
Sub GetInfo()
    Dim sResponse As String, i As Long, html As New HTMLDocument
    Dim prices As Object, info As Object
    Application.ScreenUpdating = False

    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", "https://www.fedex.com/apps/fedextrack/index.html?tracknumbers=475762806100&cntry_code=in", False
        .send
        sResponse = .responseText
    End With

    With html
        .body.innerHTML = sResponse
        Set info = .querySelectorAll("redesignStatusChevronTVC tank-results-item__data-label-large tank-text-center statusChevron_key_status")
    End With

    With Worksheets(3)
        Worksheets(3).Activate '
        For i = 0 To info.Length - 1
           Debug.Print info(i).innerText
        Next i
    End With
    Application.ScreenUpdating = True
End Sub

Ответы [ 2 ]

1 голос
/ 19 января 2020

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

Чтобы подтвердить это, попробуйте напечатать:

InStr(1, sResponse, "redesignStatusChevronTVC tank-results-item__data-label-large tank-text-center statusChevron_key_status", vbTextCompare)

до ближайшего окна, и вы должны увидеть, что оно возвращает 0 (что означает, что текст отсутствует в тексте ответа).

Информация, которую вы пытаетесь очистить (и даже элемент, который нужно выбрать с помощью .redesignStatusChevronTVC.tank-results-item__data-label-large.tank-text-center.statusChevron_key_status), динамически заполняется с помощью JavaScript и не существует в то время, когда вы пытаетесь получить к нему доступ.


Из того, что я вижу, веб-страница делает запрос HTTP POST, и сервер возвращает некоторое JSON, которое представляет информацию, относящуюся к этому номеру отслеживания. Попробуйте приведенный ниже код (запустите процедуру JustATest), которая пытается выполнить тот же HTTP-запрос POST:

Option Explicit

Private Sub JustATest()
    MsgBox "Delivery status is: " & GetDeliveryStatusForPackage("475762806100", "en_IN")
End Sub


Private Function GetDeliveryStatusForPackage(ByVal trackingNumber As String, ByVal localeValue As String)
    ' Given a "trackingNumber" and "localeValue", should return the delivery status of that package.
    Dim jsonResponse As String
    jsonResponse = GetFedExJson(trackingNumber, localeValue)

    GetDeliveryStatusForPackage = ExtractDeliveryStatusFromJson(jsonResponse)
End Function


Private Function ExtractDeliveryStatusFromJson(ByVal someJson As String) As String
    ' Should extract the delivery status. This function treats the JSON
    ' encoded string as a string and hence relies on basic string matching.
    Const START_DELIMITER As String = """keyStatus"":"""

    Dim startDelimiterIndex As Long
    startDelimiterIndex = InStr(1, someJson, START_DELIMITER)
    Debug.Assert startDelimiterIndex > 0
    startDelimiterIndex = startDelimiterIndex + Len(START_DELIMITER)

    Dim endDelimiterIndex As Long
    endDelimiterIndex = InStr(startDelimiterIndex + 1, someJson, """", vbBinaryCompare)
    Debug.Assert endDelimiterIndex > 0

    ExtractDeliveryStatusFromJson = Mid$(someJson, startDelimiterIndex, endDelimiterIndex - startDelimiterIndex)
End Function


Private Function GetFedExJson(ByVal trackingNumber As String, ByVal localeValue As String) As String
    ' Should return a JSON-encoded response. The return value can be
    ' passed to a function that parses JSON (if such a function is available for use).
    Dim formToPost As String
    formToPost = CreateFedExForm(trackingNumber, localeValue)

    Const TARGET_URL As String = "https://www.fedex.com/trackingCal/track"

    With CreateObject("MSXML2.XMLHTTP")
        .Open "POST", TARGET_URL, False
        .SetRequestHeader "Connection", "keep-alive"
        .SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        .SetRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
        .Send formToPost
        Debug.Assert InStr(1, .ResponseText, "{""TrackPackagesResponse"":{""successful"":true,", vbBinaryCompare)
        GetFedExJson = .ResponseText
    End With
End Function


Private Function CreateFedExForm(ByVal trackingNumber As String, ByVal localeValue As String) As String
    ' Should return a string representing a form of URL encoded name-value pairs.
    Dim data As String
    data = "{""TrackPackagesRequest"":{""appType"":""WTRK"",""appDeviceType"":""DESKTOP"",""supportHTML"":true,""supportCurrentLocation"":true,""uniqueKey"":"""",""processingParameters"":{},""trackingInfoList"":[{""trackNumberInfo"":{""trackingNumber"":""" & trackingNumber & """,""trackingQualifier"":"""",""trackingCarrier"":""""}}]}}"
    CreateFedExForm = "data=" & Application.EncodeURL(data) & "&action=trackpackages&locale=" & Application.EncodeURL(localeValue) & "&version=1&format=json"
End Function

  • Если это работает, то функция GetDeliveryStatusForPackage кажется способной возврата статуса доставки данных trackingNumber и localeValue.
  • Стоит отметить, что JSON, возвращаемый сервером, содержит другую информацию (которую вы не запрашивали в своем вопросе, но вы можете найти соответствующие / полезные). Публиковать здесь слишком долго, но вы можете исследовать это сами.
  • Я думаю, что можно получить информацию для нескольких номеров отслеживания в одном запросе. (Я говорю это, потому что в запросе TrackPackagesRequest.trackingInfoList является массивом, а в ответе TrackPackagesResponse.packageList также является массивом). Это просто предположение / рациональное предположение на данном этапе, но может быть то, что может потенциально сократить время, необходимое вашему коду до конечного sh.
  • Возможно, стоит получить модуль VBA (https://github.com/VBA-tools/VBA-JSON), который поддерживает синтаксический анализ JSON. Я не беспокоился, так как вы хотели только статус доставки. Но десериализация ответа будет правильным способом сделать это (особенно с точки зрения доступа к правильному пути свойства).
  • Возможно, вы также захотите проверить, явно ли запрещают условия, регулирующие использование вами их сайта. веб-очистка или любые другие подобные действия.

Что касается значения вложенного свойства keyStatus, равного "In transit" для недопустимых номеров отслеживания, проверьте путь свойства TrackPackagesResponse.packageList[0].errorList[0], где есть объект. Для недопустимых номеров отслеживания это, кажется, {"code":"1041","message":"This tracking number cannot be found. Please check the number or contact the sender."... - и для действительных номеров отслеживания свойства code и message выглядят как строки нулевой длины.

Возможно, было бы хорошо получить модуль VBA JSON, о котором я упоминал выше, поскольку имеется два объекта errorList (на разных уровнях вложенности), и вы хотите убедиться, что вы обращаетесь к нужному объекту.

Требуется изменение в код, вероятно, будет в первую очередь проверять, указывают ли свойства code и message на TrackPackagesResponse.packageList[0].errorList[0], что номер отслеживания недействителен (и возвращать message, если он недействителен). В противном случае верните TrackPackagesResponse.packageList[0].keyStatus. У меня нет времени, чтобы реализовать эти изменения прямо сейчас. Но я думаю, что это то, что вы можете сделать (если вы действительно не уверены, в таком случае дайте мне знать, с каким битом вам нужна помощь).

0 голосов
/ 19 января 2020

Спасибо, чувак Тон, который действительно решил проблему, но в этом есть некоторая практическая ошибка. Предположим, что если номер отгрузки неправильный, все равно он говорит, что имеет статус «В Trasnit»

Но веб-сайт говорит правильно говорит "Этот номер отслеживания не может быть найден, пожалуйста, проверьте номер или свяжитесь с отправителем."

При синтаксическом анализе JSON, вы ищете параметр "keyStatus" так же, как я также проверял JSON текст, сгенерированный для неправильного номера отгрузки, и я выяснил, что «keyStatus» находится в ПЕРЕХОДЕ для неправильных номеров отгрузки, также

Так что я надеюсь, что если отправление не существует, оно извлекает данные из «strings_en» . Json "(Поправьте меня, если я ошибаюсь). Снимок экрана прилагается Снимок экрана

А также для некоторых правильных номеров отгрузки реальный фактический статус может быть действительно" IN Транзит "

Так, как я выскакиваю сообщение для Неверного номера отгрузки? Любая идея?

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