无法让我的脚本继续使用IE单击“加载更多”按钮

时间:2019-03-08 19:39:52

标签: vba web-scraping internet-explorer-11

我已经使用IE在vba中创建了一个脚本,以不断单击位于网页底部的 Load more hits 按钮,直到没有此类按钮为止左按钮。

  

这是我的脚本填充该按钮的方式 :在网站的目标网页上,有一个名为Type的下拉菜单。脚本可以单击该Type来展开dropdown,然后单击选项中的某个corporate bond复选框。最后,它单击apply按钮以填充数据。但是,load more hits按钮现在可以在底部看到。

我的脚本几乎可以按照我上面描述的那样执行几乎所有步骤。我唯一要解决的问题是,单击该按钮3/4次后,脚本似乎卡住了。

如何纠正我的脚本,以继续单击该Load more hits按钮,直到没有剩下的按钮为止?

Website link

到目前为止,我已经尝试过:

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 个答案:

答案 0 :(得分:3)

我对您的网站进行了一些研究,由于我无法将所有这些信息都发表在一个评论中,因此我决定发布答案(即使它没有提供具体的解决方案,而只是提供了“答案” ”以及一些技巧)。

您问题的答案

  

如何纠正我的脚本,以继续单击该“加载更多匹配”按钮,直到没有剩余的按钮为止?

不幸的是,这不是你的错。您所定位的网站正在通过Web客户端(您的浏览器)和Web服务器之间的WebSocket通信工作,并提供您要抓取的价格。您可以看到以下内容:

enter image description here

想象一下:

  • 首次加载网页时,将初始化Web套接字并发送第一个请求(Web客户端:“嘿,服务器,请给我我的前X个结果” ,Web服务器: “当然,您可以去” )。
  • 每次单击“加载更多结果”按钮时,Web客户端(重要:重新使用相同的WS连接)都会不断向Web服务器请求X个新结果。< / li>

因此,通信持续进行了一段时间。在某些时候,无法控制,这恰好是Web套接字死了。在单击“加载更多结果”按钮时查看JavaScript控制台就足够了:您将看到请求正在进行,直到在某个时候您不仅看到引发了NullPointerException

enter image description here

如果在异常之前单击堆栈的最后一行,您会发现这是由于Web套接字造成的:

enter image description here

该错误清楚地表明:cannot read .send() on null,这意味着_ws(Web套接字)已消失。

从现在开始,您可以忘记您的网站。当您单击按钮“加载更多结果”时,Web客户端将要求Web套接字将新请求传递到Web服务器,但是Web套接字已经消失了,两者之间的通信太远了,因此(不幸地)告别了。其余数据。

您可以通过在堆栈中靠前一点来验证这一点:

enter image description here

如您在上面看到的,我们有:

  1. 刚发布新数据请求之前,登录控制台的消息为“ performSearch params ...”
  2. 新数据请求的post
  3. 刚发布新的数据请求后,在控制台中记录了一条消息,内容为“执行了搜索并显示了结果...”

虽然Web套接字仍处于活动状态,但是每次您单击“加载更多结果”时,您都会在控制台中看到以下两条消息(其他消息则显示在其其余代码中):

enter image description here

但是,在Web套接字第一次崩溃之后,无论您尝试单击多少次,您都只会收到第一条消息(Web客户端发送请求),而永远不会收到第二条消息(请求得到迷失在虚无之中):

enter image description here

请注意,这与您在VBA中观察到的行为相对应:

  

单击该按钮3/4次后,脚本似乎卡住了。

它不会卡住,实际上您的脚本可以继续正确执行。是网站超时。

我试图弄清楚为什么Web套接字崩溃了,但是没有运气。似乎只是超时(我在调试其JavaScript时遇到了很多事情,所以我的断点导致了超时),但我不能确保这是唯一的原因。由于您不控制Web客户端和Web服务器之间的过程,因此您所要做的就是希望它不会超时。

此外,我相信使用Selenium会自动设置一些较长的超时(由于执行时间长),这可以使您在一定程度上保持Web套接字对超时的容忍度。

我发现在Web套接字崩溃后恢复连接的唯一方法是完全重新加载网页并从头开始重新启动过程

我的建议

我认为您可能会构建一个XHR请求并通过JavaScript发送,因为它们的API(Web客户端/ Web套接字通过其将请求传递到Web服务器)通过其前端代码公开了。

如果打开他们的文件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;
  },

这表示您知道将POST请求发送到的URL。

请求还需要由服务器验证承载令牌。幸运的是,他们还忘记保护令牌,而提供({ORS)一个GET端点来获取令牌:

端点:https://www.boerse-stuttgart.de/api/products

响应:     {“ AuthenticationToken”:“ JgACxn2DfHceHL33uJhNj34qSnlTZu4 + hAUACGc49UcjUhmLutN6sqcktr / T634vaPVcNzJ8sHBvKvWz”,“主机”:“ frontgate.mdgms.com”}

您只需要稍微浏览一下网站即可了解POST请求的内容,然后创建一个新的XmlHttpRequest并发送其中的值即可直接从中获取价格您的VBA,而无需打开网页和自动抓取。

我建议您从文件FinderAPI.js的第66行的断点开始(代码行为this.post(this.finderEndpoint, params)params应该将您引向请求的正文-我记得您可以使用JSON.stringify(params)将对象打印为字符串。

此外,请注意,即使他们的API最多支持50个,它们也会使用500个结果的分页。换句话说,如果您将值500(而不是50)扫描到其分页属性中,该分页属性发送给API进行请求:

enter image description here

...,那么您每次将获得500个结果,而不是50个,因此,如果您决定不更深入地研究XHR解决方案,那么您的代码在抓取网页上所花费的时间将减少10分钟。

答案 1 :(得分:0)

您可以尝试更改

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

(未经测试)