[英]VBA MACRO: How can I Click on button that changes classname in HTML?
This is the website I am trying to reach.这是我试图访问的网站。 - > https://brokercheck.finra.org/ My vba code will allow me to get to the website, and input data in the cell box, but when I do, it doesn't really understand that there is text because the class changes when you input data manual, so when you do it with code, it doesn't change.
- > https://brokercheck.finra.org/我的 vba 代码将允许我访问该网站,并在单元格框中输入数据,但是当我这样做时,它并没有真正理解有文本,因为类当您手动输入数据时会发生变化,因此当您使用代码进行输入时,它不会改变。 Can anyone help please?
有人可以帮忙吗?
Set elements = html.getElementsByClassName("md-tab ng-scope ng-isolate-scope md-ink-ripple")
Set elements2 = html.getElementsByClassName("md-raised md-primary md-hue-2 md-button md-ink-ripple")
Set elements3 = html.getElementsByClassName("ng-scope selected")
Dim count As Long
Dim erow As Long count = 0
'This changes the form selection
For Each element In elements If element.className = "md-tab ng-scope ng-isolate-scope md-ink-ripple" Then element.Click
Next element
'this inputs the data on the city cell in HTML html.getElementById("acFirmLocationId").Value = "30047"
'this pushes the submit button
For Each element2 In elements2
If element2.className = "md-raised md-primary md-hue-2 md-button md-ink-ripple" Then element2.Click
Next element2
after this I get an error with the submit button because it doesn't activate the drop down list that it is embedded in the webpage.在此之后,我收到提交按钮的错误,因为它没有激活它嵌入在网页中的下拉列表。 enter image description here
在此处输入图片说明
The page makes API calls to update content based on lat and lon of zipcode.该页面根据邮政编码的经纬度进行 API 调用以更新内容。 You can find this in the network tab eg 1 , 2 .
您可以在网络选项卡中找到它,例如1 、 2 。 These API calls return a string containing json with the listings which can be parsed with a json parser after a little string manipulation|regex.
这些 API 调用返回一个包含 json 的字符串,其中的列表可以在稍加字符串操作后使用 json 解析器进行解析|正则表达式。 This means, in this case, you can issue xhr requests (so no need for I/O of opening browser) and then parse the json.
这意味着,在这种情况下,您可以发出xhr请求(因此无需打开浏览器的 I/O),然后解析 json。 The total number of results is present in the
responseText
.结果总数出现在
responseText
。
The json parser I use is Jsonconverter.bas: Download raw code from here and add to standard module called jsonConverter
.我使用的 json 解析器是 Jsonconverter.bas:从这里下载原始代码并添加到名为
jsonConverter
标准模块。 You then need to go VBE > Tools > References > Add reference to Microsoft Scripting Runtime.然后,您需要转到 VBE > 工具 > 引用 > 添加对 Microsoft 脚本运行时的引用。
The following shows returning all results (all pages) for a given zipcode - both for By Firm
and By Individual
.下面显示了返回给定邮政编码的所有结果(所有页面) - 包括
By Firm
和By Individual
。
Firms:公司:
Getting Firm
results based on zip.根据 zip 获得
Firm
结果。
The API endpoint (construction): API 端点(构造):
apiUrl = "https://api.brokercheck.finra.org/firm?hl=true&json.wrf=angular.callbacks._6&lat={LAT}&lon={LON}&nrows=100&r=25&sort=score+desc&{START}&wt=json"
uses a query string where you can alter the number of results retrieved by the changing the nrows
param.使用查询字符串,您可以在其中更改通过更改
nrows
参数检索的结果数。 The limit is 100. The default is 12. If you wish to retrieve all results, you can make subsequent calls in batches of n
eg 12 with the appropriate n
cumulative offset adjustment to start
param:限制为 100。默认为 12。如果您希望检索所有结果,您可以以
n
为批进行后续调用,例如 12,并使用适当的n
累积偏移调整来start
参数:
GET /firm?hl=true&json.wrf=angular.callbacks._7&lat=33.864146&lon=-84.114088&nrows=100&r=25&sort=score+desc&start=0&wt=json
GET /firm?hl=true&json.wrf=angular.callbacks._7&lat=33.864146&lon=-84.114088&nrows=100&r=25&sort=score+desc&start=100&wt=json
etc等等
In order to make less requests I would go with the max of n = 100 and alter the nrows
param before a loop to collect all results, and the start
(offset) param during the loop to get next batch.为了减少请求,我会使用 n = 100 的最大值并在循环之前更改
nrows
参数以收集所有结果,并在循环期间更改start
(偏移)参数以获得下一批。
Option Explicit
'Firm
Public r As Long
Public Sub GetListings()
'< VBE > Tools > References > Microsoft Scripting Runtime
Dim json As Object, apiUrl As String, re As Object, s As String, latLon()
r = 0
Set re = CreateObject("VBScript.RegExp")
apiUrl = "https://api.brokercheck.finra.org/firm?hl=true&json.wrf=angular.callbacks._6&lat={LAT}&lon={LON}&nrows=100&r=25&sort=score+desc&{START}&wt=json"
Dim xhr As Object, totalResults As Long, numPages As Long
Set xhr = CreateObject("MSXML2.XMLHTTP")
latLon = GetLatLon("30047", xhr, re) '"30047" is the zipcode of interest and could be passed as a constant set at top of module or as a local variable changed set in a loop. over zipcodes
apiUrl = Replace$(Replace$(apiUrl, "{LAT}", latLon(0)), "{LON}", latLon(1))
s = GetApiResults(xhr, Replace$(apiUrl, "{START}", "start=0"), re)
If s = "No match" Then Exit Sub
Set json = JsonConverter.ParseJson(s)("hits")
totalResults = json("total")
numPages = Application.RoundUp(totalResults / 100, 0)
Dim results(), ws As Worksheet, headers(), i As Long
ReDim results(1 To totalResults, 1 To 3)
Set ws = ThisWorkbook.Worksheets("Sheet1")
headers = Array("CRD Number", "Name", "Address")
results = GetFirmListings(results, json("hits"))
If numPages > 1 Then
For i = 2 To numPages
s = GetApiResults(xhr, Replace$(apiUrl, "{START}", "start=" & (i - 1) * 100), re)
If s = "No match" Or InStr(s, "Exceeded limit") > 0 Then Exit For
Set json = JsonConverter.ParseJson(s)("hits")
results = GetFirmListings(results, json("hits"))
Next
End If
With ws
.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
End With
End Sub
Public Function GetLatLon(ByVal zip As String, ByVal xhr As Object, ByVal re As Object) As Variant
Dim json As Object, lat As String, lon As String
With xhr
.Open "GET", Replace$("https://api.brokercheck.finra.org/locations?query={ZIP}&results=1", "{ZIP}", zip), False
.send
Set json = JsonConverter.ParseJson(.responseText)("hits")("hits")(1)("_source")
lat = json("latitude")
lon = json("longitude")
GetLatLon = Array(lat, lon)
End With
End Function
Public Function GetApiResults(ByVal xhr As Object, ByVal apiUrl As String, ByVal re As Object) As String
With xhr
.Open "GET", apiUrl, False
.send
GetApiResults = GetJsonString(re, .responseText)
End With
End Function
Public Function GetFirmListings(ByVal results As Variant, ByVal json As Object) As Variant
Dim row As Object, address As Object
Dim addressToParse As String, addressToParse2 As String
'Crd number, name and office address
For Each row In json
r = r + 1
results(r, 1) = row("_source")("firm_source_id")
results(r, 2) = row("_source")("firm_name")
addressToParse = Replace$(row("_source")("firm_ia_address_details"), "\""", Chr$(32))
addressToParse2 = Replace$(row("_source")("firm_address_details"), "\""", Chr$(32))
addressToParse = IIf(addressToParse = vbNullString, addressToParse2, addressToParse)
If addressToParse <> vbNullString Then
Set address = JsonConverter.ParseJson(addressToParse)("officeAddress")
results(r, 3) = Join$(address.items, " ,")
End If
Next
GetFirmListings = results
End Function
Public Function GetJsonString(ByVal re As Object, ByVal responseText As String) As String
With re
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "\((.*)\);" 'regex pattern to get json string
If .Test(responseText) Then
GetJsonString = .Execute(responseText)(0).SubMatches(0)
Else
GetJsonString = "No match"
End If
End With
End Function
Individuals:个人:
At page = 91 there is limit exceeded.在页面 = 91 处超出限制。 90 requests actually yielded 9,000 results of total 11,960.
90 个请求实际上产生了 11,960 个结果中的 9,000 个结果。 It may be worth investigating whether that total is actually accurate as this may be the reason for no further results.
可能值得调查该总数是否真的准确,因为这可能是没有进一步结果的原因。 For example, despite stating currently 11,960 results there are only 75 pages of 12 results per page ie only 750 of c.997 expected pages.
例如,尽管目前声明了 11,960 个结果,但每页只有 75 页,每页 12 个结果,即只有 750 页的 c.997 预期页面。 750 pages, at 12 results per page, gives 9,000 results which is the actual returned amount.
750 页,每页 12 个结果,给出 9,000 个结果,这是实际返回的数量。 The code below simply ceases looping if "limit exceeded" is found in the response.
如果在响应中发现“超出限制”,下面的代码只是停止循环。
I show extracting only specific items from json.我展示了仅从 json 中提取特定项目。 There is a lot more info returned eg all current employments which can be more than 1. You can explore, for example, the json for the first request (first 100 listing) here .
返回了更多信息,例如所有当前的就业机会可能超过 1。例如,您可以在此处浏览第一个请求(前 100 个列表)的 json。
If you are interested in a specific individual you can also use their CRD in an API call as shown at the very bottom section.如果您对特定个人感兴趣,您还可以在 API 调用中使用他们的 CRD,如最底部所示。
Option Explicit
'Individual
Public r As Long
Public Sub GetListings2()
'< VBE > Tools > References > Microsoft Scripting Runtime
Dim json As Object, apiUrl As String, re As Object, s As String, latLon()
r = 0
Set re = CreateObject("VBScript.RegExp")
apiUrl = "https://api.brokercheck.finra.org/individual?hl=true&includePrevious=false&json.wrf=angular.callbacks._d&lat={LAT}&lon={LON}&nrows=100&r=25&sort=score+desc&{START}&wt=json"
Dim xhr As Object, totalResults As Long, numPages As Long
Set xhr = CreateObject("MSXML2.XMLHTTP")
latLon = GetLatLon("30047", xhr, re)
apiUrl = Replace$(Replace$(apiUrl, "{LAT}", latLon(0)), "{LON}", latLon(1))
s = GetApiResults(xhr, Replace$(apiUrl, "{START}", "start=0"), re)
If s = "No match" Then Exit Sub
Set json = JsonConverter.ParseJson(s)("hits")
totalResults = json("total")
numPages = Application.RoundUp(totalResults / 100, 0)
Dim results(), ws As Worksheet, headers(), i As Long
'example info retrieved. There is a lot more info in json
headers = Array("CRD Number Indiv", "Name", "FINRA registered", "Disclosures", "In industry since")
ReDim results(1 To totalResults, 1 To UBound(headers) + 1)
Set ws = ThisWorkbook.Worksheets("Sheet1")
results = GetIndividualListings(results, json("hits"))
If numPages > 1 Then
For i = 2 To numPages
DoEvents
s = GetApiResults(xhr, Replace$(apiUrl, "{START}", "start=" & (i - 1) * 100), re)
If s = "No match" Or InStr(s, "Exceeded limit") > 0 Then Exit For
Set json = JsonConverter.ParseJson(s)("hits")
results = GetIndividualListings(results, json("hits"))
Next
End If
With ws
.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
End With
End Sub
Public Function GetLatLon(ByVal zip As String, ByVal xhr As Object, ByVal re As Object) As Variant
Dim json As Object, lat As String, lon As String
With xhr
.Open "GET", Replace$("https://api.brokercheck.finra.org/locations?query={ZIP}&results=1", "{ZIP}", zip), False 'changed results = 10 to results = 1
.send
Set json = JsonConverter.ParseJson(.responseText)("hits")("hits")(1)("_source")
lat = json("latitude")
lon = json("longitude")
GetLatLon = Array(lat, lon)
End With
End Function
Public Function GetApiResults(ByVal xhr As Object, ByVal apiUrl As String, ByVal re As Object) As String
With xhr
.Open "GET", apiUrl, False
.send
GetApiResults = GetJsonString(re, .responseText)
End With
End Function
Public Function GetIndividualListings(ByVal results As Variant, ByVal json As Object) As Variant
Dim row As Object
'can have numerous current employments. Alter here and below if want more info from json about the individual
For Each row In json
r = r + 1
results(r, 1) = row("_source")("ind_source_id")
results(r, 2) = Replace$(Join$(Array(row("_source")("ind_firstname"), row("_source")("ind_middlename"), row("_source")("ind_lastname")), ", "), ", , ", ", ")
results(r, 3) = row("_source")("ind_approved_finra_registration_count")
results(r, 4) = row("_source")("ind_bc_disclosure_fl")
results(r, 5) = row("_source")("ind_industry_cal_date")
Next
GetIndividualListings = results
End Function
Public Function GetJsonString(ByVal re As Object, ByVal responseText As String) As String
With re
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "\((.*)\);" 'regex pattern to get json string
If .Test(responseText) Then
GetJsonString = .Execute(responseText)(0).SubMatches(0)
Else
GetJsonString = "No match"
End If
End With
End Function
Single individual:单身人士:
Detailed info for a single individual can be gained from:可以从以下位置获得单个人的详细信息:
https://api.brokercheck.finra.org/individual/1614374?json.wrf=angular.callbacks._h&wt=json
By Whatever I understood of your problem.不管我对你的问题的理解。 Maybe you can make use of
Application.SendKeys
.也许您可以使用
Application.SendKeys
。 That's what I use when I need to input any information on Web.这就是我需要在 Web 上输入任何信息时使用的方法。
Code: You can manipulate it as per your need.代码:您可以根据需要对其进行操作。
Sub gottt()
Dim ie As Object
Dim el As Object
Set ie = CreateObject("InternetExplorer.Application")
ie.navigate "https://brokercheck.finra.org/"
ie.Visible = True
Do While ie.Busy
Application.Wait DateAdd("s", 1, Now)
Loop
For Each el In ie.document.getElementsByTagName("span")
If el.innerText = "Firm" Then el.Click
Next
Application.Wait DateAdd("s", 1, Now)
For Each el In ie.document.getElementsByTagName("input")
If el.getAttribute("name") = "acFirmLocation" Then
el.Focus
Application.SendKeys ("30047"), True
Application.Wait DateAdd("s", 1, Now)
End If
Next
Application.Wait DateAdd("s", 1, Now)
For Each el In ie.document.getElementsByClassName("md-raised md-primary md-hue-2 md-button md-ink-ripple")
If el.getAttribute("aria-label") = "FirmSearch" Then el.Click
Next
End Sub
Demo:演示:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.