Невозможно, чтобы мой скрипт продолжал нажимать на кнопку Загрузить больше, используя IE - PullRequest
5 голосов
/ 08 марта 2019

Я создал скрипт в vba, используя IE, чтобы продолжать нажимать кнопку Load more hits, расположенную в нижней части веб-страницы, до тех пор, пока не останется такой кнопки.

Вот как мой скрипт может заполнить эту кнопку : На целевой странице сайта есть раскрывающийся список с именем Type. Сценарий может щелкнуть по этому Type, чтобы развернуть dropdown, после чего он щелкает по некоторому флажку corporate bond среди параметров. Наконец, он нажимает кнопку apply, чтобы заполнить данные. Однако эта кнопка load more hits теперь может быть видна внизу.

Мой сценарий может выполнять почти все шаги в точности так, как я описал выше. Единственное, что я пытаюсь решить, это то, что скрипт застревает после нажатия на эту кнопку 3/4 раза.

Как я могу исправить свой скрипт, продолжая нажимать на эту кнопку Load more hits, пока такой кнопки не осталось?

Ссылка на сайт

Я уже пробовал:

Sub ExhaustLoadMore()
    Dim IE As New InternetExplorer, I As Long
    Dim Html As HTMLDocument, post As Object, elem As Object
    Dim CheckBox As Object, btnSelect As Object

    With IE
        .Visible = True
        .navigate "https://www.boerse-stuttgart.de/en/tools/product-search/bonds"
        While .Busy Or .readyState < 4: DoEvents: Wend
        Set Html = .document

        Do: Loop Until Html.querySelectorAll(".bsg-loader-ring__item").Length = 0

        Html.querySelector("#bsg-filters-btn-bgs-filter-3").Click
        Do: Set CheckBox = Html.querySelector("#bsg-checkbox-3053"): DoEvents: Loop While CheckBox Is Nothing
        CheckBox.Click

        Set btnSelect = Html.querySelector("#bsg-filters-menu-bgs-filter-3 .bsg-btn__label")
        Do: Loop While btnSelect.innerText = "Close"
        btnSelect.Click

        Do: Loop Until Html.querySelectorAll(".bsg-loader-ring__item").Length = 0
        Do: Set elem = Html.querySelector(".bsg-table__tr td"): DoEvents: Loop While elem Is Nothing

        Do
            Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
            If Not post Is Nothing Then
                post.ScrollIntoView
                post.Click
                Application.Wait Now + TimeValue("00:00:05")
            Else: Exit Do
            End If
        Loop
    End With
End Sub

Я пробовал с селеном, но это, кажется, намного медленнее. Тем не менее, он продолжает нажимать на кнопку «Загрузить больше» после долгого ожидания, даже если в нем нет жестко заданного ожидания. В случае селена: я хотел бы иметь какое-либо решение, которое могло бы помочь сократить его время выполнения.

Sub ExhaustLoadMore()
    Const Url$ = "https://www.boerse-stuttgart.de/en/tools/product-search/bonds"
    Dim driver As New ChromeDriver, elem As Object, post As Object

    With driver
        .get Url
        Do: Loop Until .FindElementsByCss(".bsg-loader-ring__item").count = 0
        .FindElementByCss("#bsg-filters-btn-bgs-filter-3", timeOut:=10000).Click
        .FindElementByXPath("//label[contains(.,'Corporate Bond')]", timeOut:=10000).Click
        .FindElementByXPath("//*[@id='bsg-filters-menu-bgs-filter-3']//button", timeOut:=10000).Click
        Do: Loop Until .FindElementsByCss(".bsg-loader-ring__item").count = 0
        Set elem = .FindElementByCss(".bsg-table__tr td", timeOut:=10000)
        Do
            Set post = .FindElementByCss(".bsg-searchlist__load-more button.bsg-btn--juna", timeOut:=10000)
            If Not post Is Nothing Then
                post.ScrollIntoView
                .ExecuteScript "arguments[0].click();", post
                Do: Loop Until .FindElementsByCss("p.bsg-searchlist__info--load-more").count = 0
            Else: Exit Do
            End If
        Loop
        Stop
    End With
End Sub

Ответы [ 2 ]

3 голосов
/ 17 марта 2019

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

Ответ на ваш вопрос

Как я могу исправить свой скрипт, чтобы он продолжал нажимать на кнопку «Загрузить больше хитов», пока такой кнопки не осталось?

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

enter image description here

Вообразите это так:

  • Когда вы впервые загружаете свою веб-страницу, веб-сокет инициализируется и отправляется первый запрос (веб-клиент: "Эй, сервер, дай мне первые X результатов" , веб-сервер: " Конечно, вы идете ").
  • Каждый раз, когда вы нажимаете кнопку «Загрузить дополнительные результаты», веб-клиент ( важно: повторное использование того же WS-соединения ) продолжает запрашивать X новых результатов для веб-сервера.

Итак, связь продолжается некоторое время. В какой-то момент, вне вашего контроля , случается, что веб-сокет просто умирает. Достаточно взглянуть на консоль JavaScript, нажимая кнопку «Загрузить дополнительные результаты»: вы увидите, что запрос выполняется до тех пор, пока в какой-то момент вы не увидите просто поднятый NullPointerException:

enter image description here

Если вы нажмете на последнюю строку стека перед исключением, вы увидите, что это из-за веб-сокета:

enter image description here

Ошибка ясно говорит: cannot read .send() on null, что означает, что _ws (веб-сокет) пропал.

С этого момента вы можете забыть о своем сайте. Когда вы нажимаете кнопку «Загрузить дополнительные результаты», веб-клиент попросит веб-сокет доставить новый запрос на веб-сервер, но веб-сокет пропал, так что прощайте связь между ними, и, к сожалению, до свидания остальные ваши данные.

Вы можете проверить это, просто поднявшись немного выше в стеке:

enter image description here

Как вы можете видеть выше, у нас есть:

  1. Сообщение, зарегистрированное в консоли с надписью "executeSearch params ...") непосредственно перед отправкой нового запроса данных
  2. post нового запроса данных
  3. Сообщение, зарегистрированное в консоли с надписью "выполнил поиск с результатом ...") сразу после публикации нового запроса данных

Пока веб-сокет еще активен, каждый раз, когда вы нажимаете «Загрузить больше результатов», вы видите эти два сообщения в консоли (с другими сообщениями между ними, напечатанными поверх остальной части их кода):

enter image description here

Однако после первого сбоя веб-сокета, независимо от того, сколько раз вы пытаетесь нажать на кнопку, вы получите только первое сообщение (веб-клиент отправляет запрос), но никогда не получит второе сообщение (запрос получает потерял в пустоте):

enter image description here

Обратите внимание, это соответствует вашему поведению, наблюдаемому в VBA:

скрипт застревает после нажатия на эту кнопку 3/4 раза.

Он не застревает, на самом деле ваш скрипт продолжает работать правильно. Время ожидания истекло.

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

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

Единственный способ восстановить соединение после сбоя веб-сокета - это полная перезагрузка веб-страницы и перезапуск процесса с нуля .

Мои предложения

Я думаю, что вы могли бы пойти с созданием XHR-запроса и отправкой через JavaScript, потому что их API (через который веб-клиент / веб-сокет доставляет запрос на веб-сервер) довольно сильно представлены в их коде переднего плана.

Если вы откроете их файл FinderAPI.js, вы увидите, что они оставили конечные точки и конфигурации API зашифрованными:

var FinderAPI = {
  store: null,
  state: null,
  finderEndpoint: '/api/v1/bsg/etp/finder/list',
  bidAskEndpoint: '/api/v1/prices/bidAsk/get',
  instrumentNameEndpoint: '/api/products/ProductTypeMapping/InstrumentNames',
  nameMappingEndpoint: '/api/v1/bsg/general/namemapping/list',
  apiConfig: false,
  initialize: function initialize(store, finderEndpoint) {
    var apiConfig = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    this.store = store;
    this.state = store.getState();
    this.apiConfig = apiConfig;
    this.finderEndpoint = finderEndpoint;
  },

Это означает, что вы знаете URL-адрес, на который следует отправить запрос POST.

Для запроса также требуется, чтобы токен на предъявителя был проверен сервером. К счастью, они также забыли защитить свои токены, предоставив (GORSH) конечную точку GET для получения токена:

Конечная точка: https://www.boerse -stuttgart.de / api / products

Ответ: {"AuthenticationToken": "JgACxn2DfHceHL33uJhNj34qSnlTZu4 + hAUACGc49UcjUhmLutN6sqcktr / T634vaPVcNzJ8sHBvKvWz", "Хост": "frontgate.m} 11" "

Вам просто нужно немного поиграть с веб-сайтом, чтобы выяснить, каково тело вашего POST-запроса, затем создать новый XmlHttpRequest и отправить эти значения внутри него, чтобы получить цены непосредственно в вашем VBA. без открытия веб-страницы и роботизированной очистки.

Я предлагаю вам начать с точки останова в файле FinderAPI.js, строка 66 (строка кода this.post(this.finderEndpoint, params), params должна привести вас к телу запроса - я помню, вы можете напечатать объект как строка с JSON.stringify(params)).

Также обратите внимание, что они используют нумерацию результатов 50 каждый раз, хотя их API поддерживает до 500 из них. Другими словами, если вам нужно ввести значение 500 (вместо 50) в их свойство разбиения на страницы, отправленное в API для запроса:

enter image description here

... тогда вы получите 500 результатов за раз вместо 50, поэтому на 10 раз сократится время, которое ваш код потратит на очистку веб-страницы, если вы решите не углубляться в решение XHR.

0 голосов
/ 10 марта 2019

Не могли бы вы попробовать изменить

Do
    Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
    If Not post Is Nothing Then
      post.ScrollIntoView
    post.Click
    Application.Wait Now + TimeValue("00:00:05")
    Else: Exit Do
  End If
Loop

до:

Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
If Not post Is Nothing Then
      post.ScrollIntoView
      While Not post Is Nothing
        Debug.Print "Clicking"
        post.Click
        Application.Wait Now + TimeValue("00:00:05")
      Wend
      Debug.Print "Exited Click"
End If

(непроверенные)

...