Очистите веб-сайт с помощью XML-запроса HTTP с помощью Excel VBA: дождитесь полной загрузки страницы - PullRequest
0 голосов
/ 05 сентября 2018

Я пытаюсь вычеркнуть цену продукта с веб-страницы, используя Excel VBA. Следующий код работает при использовании запроса навигации VBA Internet Explorer. Однако вместо этого я бы хотел использовать HTTP-запрос XML для ускорения процесса очистки.

В коде запроса IE я говорю приложению подождать 3 секунды, чтобы страница полностью загрузилась и была в состоянии оценить цену продукта. Если эта строка не указана, цена не будет найдена.

Я пытался изменить это с помощью HTTP-запроса XML (см. Второй код), но безуспешно. Цена не найдена. Кажется, что код пытается очистить страницу до того, как она будет полностью загружена.

Как настроить код HTTP-запроса XML так, чтобы он находил цену продукта (и начинал поиск / просмотр только после полной загрузки страницы (и сценариев)?

Работает следующий код запроса IE: (немедленная отладка. печать цены продукта)

Sub Get_Product_Price_AH_IE()

Dim IE As New SHDocVw.InternetExplorer
Dim HTMLDoc As MSHTML.HTMLDocument

Dim AHArticles As MSHTML.IHTMLElementCollection
Dim AHArticle As MSHTML.IHTMLElement

Dim AHEuros As MSHTML.IHTMLElementCollection
Dim AHCents As MSHTML.IHTMLElementCollection

Dim AHPriceEuro As Double
Dim AHPriceCent As Double
Dim AHPrice As Double


IE.Visible = False
IE.navigate "https://www.ah.nl/producten/product/wi3640/lu-bastogne-biscuits-original"


    Do While IE.readyState <> READYSTATE_COMPLETE
    Loop

    Set HTMLDoc = IE.document

'wait for the page to fully load to be able to get price data
Application.Wait Now + #12:00:03 AM#


Set AHArticles = HTMLDoc.getElementsByTagName("article")

For Each AHArticle In AHArticles

 If AHArticle.getAttribute("data-sku") = "wi3640" Then

        Set AHEuros = AHArticle.getElementsByClassName("price__integer")
        Set AHCents = AHArticle.getElementsByClassName("price__fractional")

       AHPriceEuro = AHEuros.Item(0).innerText
       AHPriceCent = AHCents.Item(0).innerText

      AHPrice = AHPriceEuro + (AHPriceCent / 100)

Debug.Print AHPrice

            Exit For
        End If


Next AHArticle

IE.Quit

End Sub

Следующий XML-запрос HTTP не дает желаемого результата (цена не отображается на экране немедленной отладки):

Sub Get_Product_Price_AH_XML()

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

Dim AHArticles As MSHTML.IHTMLElementCollection
Dim AHArticle As MSHTML.IHTMLElement

Dim AHEuros As MSHTML.IHTMLElementCollection
Dim AHCents As MSHTML.IHTMLElementCollection

Dim AHPriceEuro As Double
Dim AHPriceCent As Double
Dim AHPrice As Double


XMLReq.Open "GET", "https://www.ah.nl/producten/product/wi3640/lu-bastogne-biscuits-original", False
XMLReq.send


If XMLReq.Status <> 200 Then
    MsgBox "Problem" & vbNewLine & XMLReq.Status & " - " & XMLReq.statusText
    Exit Sub
    End If

HTMLDoc.body.innerHTML = XMLReq.responseText


Application.Wait Now + #12:00:03 AM#


Set AHArticles = HTMLDoc.getElementsByTagName("article")

For Each AHArticle In AHArticles

 If AHArticle.getAttribute("data-sku") = "wi3640" Then

        Set AHEuros = AHArticle.getElementsByClassName("price__integer")
        Set AHCents = AHArticle.getElementsByClassName("price__fractional")

       AHPriceEuro = AHEuros.Item(0).innerText
       AHPriceCent = AHCents.Item(0).innerText

      AHPrice = AHPriceEuro + (AHPriceCent / 100)

Debug.Print AHPrice

            Exit For
        End If


Next AHArticle


End Sub

Спасибо за вашу помощь!

1 Ответ

0 голосов
/ 05 сентября 2018

HTTP-запрос REST API:

Ваш текущий метод не позволяет загружать страницу полностью, как вы заметили. Вы можете сформулировать REST API XMLHTTPrequest , используя URLEncode для передачи закодированной строки URL в API. Сервер отправляет обратно ответ JSON, содержащий значение, которое вы ищете, а также множество другой информации.

Я демонстрирую два метода извлечения информации о цене из возвращенной строки JSON: ① Использование функции Split для извлечения цены путем генерации подстрок до тех пор, пока не останется нужная строка; ② Использование JSONParser для навигации по структуре JSON и возврата требуемого значения.

Код:

Следующее использует Split для извлечения значения.

Option Explicit
Public Sub GetPrice()
    Const BASE_URL As String = "https://www.ah.nl/service/rest/delegate?url="
    Dim URL As String, sResponse As String, price As String
    URL = BASE_URL & Application.WorksheetFunction.EncodeURL("/producten/product/wi3640/lu-bastogne-biscuits-original")

    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", URL, False
        .send
        sResponse = StrConv(.responseBody, vbUnicode)
    End With
    price = Split(Split(sResponse, """now"":")(1), "}")(0)
    Debug.Print price
End Sub

Анализ ответа JSON:

Использование Split:

Вы можете прочитать весь ответ JSON в объект JSON, используя анализатор JSON, например JSONConverter.bas . Затем проанализируйте этот объект по цене. Я обнаружил, что проще использовать функцию Split для извлечения необходимой информации, показанной ниже:

image

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

В строке

price = Split(Split(sResponse, """now"":")(1), "}")(0)

У меня есть два вложенных оператора Split. Они последовательно разбивают ответную строку JSON, чтобы извлечь цену 1.55.

В первом разделении используется "now": в качестве разделителя, в результате чего получается массив следующим образом:

enter image description here

Целевая цена, которую вы видите, находится в строке в позиции 1.

Итак, эта строка извлекается с помощью:

Split(sResponse, """now"":")(1)

Затем нам нужно получить только цену, поэтому снова используйте Split, чтобы получить 1.55, используя разделитель "}":

Split(Split(sResponse, """now"":")(1), "}")

В результате получается следующий массив (укороченный как довольно длинный):

enter image description here

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

price = Split(Split(sResponse, """now"":")(1), "}")(0)

Использование анализатора JSON:

Если вы хотите пересечь структуру json, вы должны использовать следующее:

Dim json As Object
Set json = JsonConverter.ParseJson(sResponse)("_embedded")("lanes")(5)("_embedded")("items")(1)("_embedded")("product")("priceLabel")
Debug.Print json("now")

После загрузки и добавления JSONConverter.bas вы добавляете ссылку на Microsoft Scripting Runtime через VBE > Tools > References. выше Set json кодовое выражение представляет путь к цене, как видно из структуры JSON ниже. Я свернул некоторые детали, чтобы прояснить путь. Вы должны вставить вышеуказанную пару строк в исходный код вместо строки Split.

JSON Path

На приведенной выше схеме [] обозначает объект collection, к которому необходимо получить доступ по индексу, например, JsonConverter.ParseJson(sResponse)("_embedded")("lanes")(5). {} обозначает объект dictionary, к которому можно получить доступ, например, с помощью клавиши, например. JsonConverter.ParseJson(sResponse)("_embedded")("lanes")(5)("_embedded"). Синтаксис в моей строке

Set json = JsonConverter.ParseJson(sResponse)("_embedded")("lanes")(5)("_embedded")("items")(1)("_embedded")("product")("priceLabel")

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

...