我已经使用IE在vba中创建了一个脚本,以不断单击位于网页底部的 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
答案 0 :(得分:3)
我对您的网站进行了一些研究,由于我无法将所有这些信息都发表在一个评论中,因此我决定发布答案(即使它没有提供具体的解决方案,而只是提供了“答案” ”以及一些技巧)。
如何纠正我的脚本,以继续单击该“加载更多匹配”按钮,直到没有剩余的按钮为止?
不幸的是,这不是你的错。您所定位的网站正在通过Web客户端(您的浏览器)和Web服务器之间的WebSocket通信工作,并提供您要抓取的价格。您可以看到以下内容:
想象一下:
因此,通信持续进行了一段时间。在某些时候,无法控制,这恰好是Web套接字死了。在单击“加载更多结果”按钮时查看JavaScript控制台就足够了:您将看到请求正在进行,直到在某个时候您不仅看到引发了NullPointerException
:
如果在异常之前单击堆栈的最后一行,您会发现这是由于Web套接字造成的:
该错误清楚地表明:cannot read .send() on null
,这意味着_ws
(Web套接字)已消失。
从现在开始,您可以忘记您的网站。当您单击按钮“加载更多结果”时,Web客户端将要求Web套接字将新请求传递到Web服务器,但是Web套接字已经消失了,两者之间的通信太远了,因此(不幸地)告别了。其余数据。
您可以通过在堆栈中靠前一点来验证这一点:
如您在上面看到的,我们有:
post
虽然Web套接字仍处于活动状态,但是每次您单击“加载更多结果”时,您都会在控制台中看到以下两条消息(其他消息则显示在其其余代码中):
但是,在Web套接字第一次崩溃之后,无论您尝试单击多少次,您都只会收到第一条消息(Web客户端发送请求),而永远不会收到第二条消息(请求得到迷失在虚无之中):
请注意,这与您在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进行请求:
...,那么您每次将获得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
(未经测试)