VBA access drop downs properly in a website - html

I'm having a bit of a problem. I'm hoping some VBA guru's can help me with. I have a website that has drop down options I'd like to be able to select. Right now my code is off and I'm not sure what I'm doing wrong. I looked over the website trying to find out what I was doing wrong but nothing I found that could answer my question directly. Any help would be greatly appreciated.
Here is what I have:
Private Sub CMReportExport()
Dim IEapp As Object
Dim WebUrl As String
Dim yearList As Object
Dim prefixList As Object
Dim versionList As Object
Set IEapp = CreateObject("InternetExplorerMedium.Application")
'Set IEapp = CreateObject("InternetExplorer.Application")
WebUrl = "http://reporthub/Enterprise/Pages/Report.aspx?ItemPath=%2fSupply+Chain%2fProduction%2fContribution+Margin%2fNonFood%2fNonFood+CM+RetailCat"
With IEapp
.Silent = True
.Visible = True
.navigate WebUrl
End With
While IEapp.Busy Or IEapp.readyState < 4: DoEvents: Wend '<== Ensure page loaded
Set yearList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl03_ddValue option") '<==apply CSS selector to get nodeList
Set prefixList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl05_ddValue option")
Set versionList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl07_ddValue option")
yearList.item(2).Selected = True 'Index into nodeList e.g. second item is at index 2 = year 2018
prefixList.item(2).Selected = True
versionList.item(1).Selected = True
'Set IEapp.getElementById("ctl32_ctl04_ctl03_ddValue").selectedvalue = 2 'Year
'Set IEapp.getElementById("ctl32_ctl04_ctl05_ddValue").selectedvalue = DA 'Prefix
'Set IEapp.getElementById("ctl32_ctl04_ctl07_ddValue").selectedvalue = 1 'Version
End Sub
The HTML elements (a chunk) are as follows:
<tr>
<td class="ParamLabelCell"><label for="ctl32_ctl04_ctl03_ddValue"><span>Year</span></label>
</td>
<td class="ParamEntryCell" style="padding-right:0px;"><div id="ctl32_ctl04_ctl03">
<select name="ctl32$ctl04$ctl03$ddValue" onchange="javascript:setTimeout('__doPostBack(\'ctl32$ctl04$ctl03$ddValue\',\'\')', 0)" id="ctl32_ctl04_ctl03_ddValue" disabled="disabled">
<option selected="selected" value="0"><Select a Value></option>
<option value="1">2019</option>
<option value="2">2018</option>
<option value="3">2017</option>
<option value="4">2016</option>
<option value="5">2015</option>
</select>
</div></td><td class="InterParamPadding"></td><td class="ParamLabelCell"><label for="ctl32_ctl04_ctl05_ddValue"><span disabled="disabled">Offer</span></label></td><td class="ParamEntryCell" style="padding-right:0px;"><div id="ctl32_ctl04_ctl05">
<select name="ctl32$ctl04$ctl05$ddValue" onchange="javascript:setTimeout('__doPostBack(\'ctl32$ctl04$ctl05$ddValue\',\'\')', 0)" id="ctl32_ctl04_ctl05_ddValue" disabled="disabled" class="EmptyDropDown">
</select>
</div>
</td>
</tr>
<tr IsParameterRow="true">
<td class="ParamLabelCell"><label for="ctl32_ctl04_ctl07_ddValue"><span disabled="disabled">Version</span></label></td>
<td class="ParamEntryCell" style="padding-right:0px;"><div id="ctl32_ctl04_ctl07">
<select name="ctl32$ctl04$ctl07$ddValue" id="ctl32_ctl04_ctl07_ddValue" disabled="disabled" class="EmptyDropDown">
</tr>

Caveat:
I can't test against that webpage and there is not enough HTML to know if there are forms/frames and the like to also negotiate. The following is based on the HTML snippet provided. Mileage may vary.
CSS selectors:
You can use CSS selectors to target elements. For example you can get all the year options with the following selector, and then index into the returned nodeList:
#ctl32_ctl04_ctl03_ddValue option
The above selector says all elements with option tag that have parent element with id ctl32_ctl04_ctl03_ddValue. "#" is the id selector.
There is not enough HTML to advise on the other elements you are targeting for sure but see my suggestions at the bottom. This illustrates the principle of using CSS selectors to target elements by page styling. More info on CSS selectors here and here.
CSS query results on HTML section provided:
Getting the nodeList and selected an option - VBA:
As more than one element is matched, querySelectorAll method is used to apply the selector and return the nodeList of matching elements.
For example:
While IEapp.Busy Or IEapp.readyState < 4: DoEvents: Wend '<== Ensure page loaded
Dim yearList As Object
Set yearList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl03_ddValue option") '<==apply CSS selector to get nodeList
yearList.item(1).Selected = True 'Index into nodeList e.g. second item is at index 1 = year 2019
Assuming similar pattern for your other two option lists:
Dim prefixList As Object, versionList As Object
Set prefixList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl05_ddValue option")
Set versionList = IEapp.document.querySelectorAll("#ctl32_ctl04_ctl07_ddValue option")
Then index into those.
If problems with While IEapp.Busy Or IEapp.readyState < 4: DoEvents: Wend then try the following instead:
While IEapp.readyState < 4: DoEvents: Wend
Dim waitUntil As Date
waitUntil = Now + TimeValue("00:00:11") '<==Adjust additional wait time in seconds here
Do
DoEvents
Loop Until Now >= waitUntil
XMLHttpRequest (XHR)
You are currently using IE browser which is a slow method for scraping. It may be possible for you to retrieve the required info by issuing an XHR POST request. I can't test against that page but you can find more info here and here.

Related

I can't select multiple items in dropdown list web VBA

I am trying to fill in a form from an intranet page.
I have managed to highlight the element from a dropdown list I want to select but not at the same time. I want to select three in list
Here's my code:
Sub GetLTRTable()
Dim ie As Object
Set ie = CreateObject("InternetExplorer.application")
ie.Visible = True
ie.navigate "myurl"
Do Until ie.ReadyState = READYSTATE_COMPLETE
DoEvents
Loop
Dim Fields As Object
Set Fields = ie.Document.all.Item("ADFields")
Fields.Checked = True
Dim organization As Object
Set organization = ie.Document.all.Item("Org")
organization.selectedindex = 0
Dim Modality As Object
Set Modality = ie.Document.getElementsByName("Modality")(0)
With Modality.Value = "'GDXE'"
Modality.Value = "'ABUS'"
Modality.Value = "'A&S'"
End With
ie.Document.getElementsByName("Action").Item(1).Click
Here's the source code (note that I have just put an extract since there are a lot of modalities):
<select name="Modality" multiple="" size="4"><option value="'ALL'" selected="">ALL</option>
<option value="'A&S'">A&S</option>
<option value="'ABUS'">ABUS</option>
<option value="'ACS'">ACS</option>
<option value="'ANES'">ANES</option>
</select>
Thanks for the help
Corentin
If you want to use by value then use attribute = value css selectors
With ie.document
.querySelector("[value='ABUS']").Selected = True
.querySelector("[value='GDXE']").Selected = True
End With
This should prove a fast selector method.
Also, use a proper page wait:
While ie.Busy Or ie.readyState < 4: DoEvents: Wend
In this case, you don't need to change the value of Modality, but set the options to "selected":
Modality.getElementsByTagname("option").item(0).selected = "selected"

Object Variable or With Block Variable not set getelementsbyname vba

I am trying to pull the data from websites. So I want to select 3 drop-down values in below URL but i cant change those values. example i want to select month
<select name="fmonth1" id="fmonth1" class="dropdownboxlang" size="1" style="width:60px;">
<option value="0">MM</option>
<option value="1">Jan</option>
<option value="2">Feb</option>
<option value="3">Mar</option>
<option value="4">Apr</option>
<option value="5">May</option>
<option value="6">Jun</option>
<option value="7">Jul</option>
<option value="8">Aug</option>
<option value="9">Sep</option>
<option value="10">Oct</option>
<option value="11">Nov</option>
<option value="12">Dec</option>
</select>
the error i am getting while changing the value in drop-down. I am trying all the possible ways from two days. Any suggestion would be appreciated.
Public Sub bse()
Dim IE As InternetExplorer
Dim HTML As HTMLDocument
Dim Dropdown As IHTMLElement
Dim dropOption As IHTMLElement
Set IE = CreateObject("InternetExplorer.Application")
With IE
.Visible = True
.Navigate "https://www.bseindia.com/markets/debt/BhavCopyDebt.aspx?expandable=6"
End With
Do
DoEvents
Application.Wait Now() + TimeValue("00:00:01")
Loop Until IE.ReadyState = 4 And Not IE.Busy
Set HTML = IE.Document
HTML.getElementsByName("fmonth1")(0).Value = "1" error line
IE.Quit
End Sub
You can use css id selector in descendant combination with attribute = value selector
#fmonth1 option[value='1']
That is:
ie.document.querySelector("#fmonth1 option[value='1']")
You may need .Click on the end for selected. Can't test that url but also try:
ie.document.querySelector("#fmonth1 option[value='1']").Selected = True
More generally, if you know an element exists, and your syntax is correct, but you are still getting not set then it may be a timing issue where you need a longer wait (e.g. timed loop) before attempting to access, e.g.
Const MAX_WAIT_SEC As Long = 10
Dim t As Date, ele As Object
t = Timer
Do
DoEvents
On Error Resume Next
Set ele = ie.document.querySelector("#fmonth1")
On Error GoTo 0
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While ele Is Nothing
If Not ele Is Nothing Then
ele.Click 'may be needed to expose options
ie.document.querySelector("#fmonth1 option[value='1']").Selected = True
End If
If inside of a parent iframe/frame you will need to navigate that first e.g.
ie.document.getElementsByTagName("iframe")(0).contentDocument.querySelector("#fmonth1 option[value='1']").Selected = True
I tested the following now I can access the page:
Public Sub MakeSelection()
Dim ie As New InternetExplorer
With ie
.Visible = True
.navigate "https://www.bseindia.com/markets/debt/BhavCopyDebt.aspx?expandable=6"
While .Busy Or .readyState < 4: DoEvents: Wend
.document.getElementsByTagName("iframe")(0).contentDocument.querySelector("#fmonth1 option[value='1']").Selected = True
Stop '<==Delete me later
.Quit
End With
End Sub

Interaction With WebPage VBA

I'm creating a code that allows me to open a specific site and enter value in element with the name searchByTagName=searchByTRSMP and then Type search to load the new window
But the problem that button search doesn't have a TagName or IdName only this
<td width="13%" align="center" rowSpan="1" colSpan="1">
<input name="" onclick="Javascript:document.forms[0].submit();return false;" type="image" srx="/cmh/cmh/xxxxxx" border="0"></input>
Anyone Can Light me on pressing that button with only those conditions
this Mycode :
Sub ToComb()
Dim ie As Object
Dim itm As IHTMLElement
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = True
ie.navigate "http://XXXX-
XXXX.eu.airbus.XXXXXp:XXXXX/cmh/consultation/preSearchTRSTDMAS.do?
clearBackList=true&CMH_NO_STORING_fromMenu=true"
While ie.Busy Or ie.readyState <> 4: DoEvents: Wend
Set itm = ie.document.getElementsByName("searchByTRSMP")(0)
If Not itm Is Nothing Then itm.Value = "k20734"
Set Doc = ie.document
Set tags = ie.document.getElementsByTagName("")
For Each tagx In tags
If tagx.Value = "Next" Then
tagx.Click
Exit For
End If
Next
End Sub
GetElementsByTagname mean search for an element by the type of an HTML element (such as, div, p or in your example - input).
You can get all your inputs tags (elements), iterate them and identify the required input, based on it's (for example) srx attribute:
Set tags = ie.Document.GetElementsByTagname("Input")
For Each tagx In tags
If tagx.src= "/cmh/cmh/xxxxxx" Then
tagx.Click
End If
Next
In addition, the final src of the input might changed from the actual code, because you use a relative path. Check the actual src with a MsgBox:
For Each tagx In tags
MsgBox tagx.src
I assume it will be different, such as prefix of http and so on:
If tagx.src = "http://xxxx
xxxx.eu.airbus.xxx:xxxx/cxxx/xxx/image/button_search.gif" Then

Get value from web document input element with VBA

I am having difficult to retrieve value 300 from the input named points.
Here's my HTML and VBA code.
HTML:
<td id="myPower_val_9" style="visibility: visible;">
<input type="text" disabled="disabled" value="300" name="points"></input>
</td>
VBA:
Dim ie As Object
Dim myPoints As String
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 0
.navigate "www.example.com"
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByTagName("td")(0).getElementById("myPoints").innerText)
Range("A1").Value = myPoints
HTML Code
I'd try working out the code that manipulates the Document Object Model (DOM) in javascript in a web browser so you can make use of better web based debugging tools.
There are several issues here that a console or debugger could help out with:
You want to get the element ID myPoints but in HTML it's just called points
You want to get the element by ID, but you've only set the name property -
As long as name is unique to the element, you don't need to search for a td first
As you can see from <input></input>, input elements do not have innerText (the text inside the ><). Instead they have a value attribute
The element exposes it's attributes and other data through the properties on the object itself. So you can check the input's value by just looking at .value
Here's a javascript example of what you're trying to do:
var value = document.getElementsByName("points")[0].value;
console.log(value);
<input type="text" disabled="disabled" value="300" name="points" />
Open the console (F12), and you should see 300
VBA
To convert it to VBA code for Excel, just make sure you uses parentheses () for VB arrays instead of square brackets [] for JS arrays:
myPoints = Trim(Doc.getElementsByName("points")(0).Value)
That should work just fine.
References
Since I'm not sure at what point you're failing in VB, also make sure you have all the proper web references in place in your VBA script.
Go to Tools > References > and add "Microsoft HTML Object Library" and "Microsoft Internet Controls":
Demo
I created a demo in plunker so there would be a live site to go against instead of example.com.
Paste the following code into excel and everything should work fine:
Public Sub GetValueFromBrowser()
Dim ie As Object
Dim url As String
Dim myPoints As String
url = "http://run.plnkr.co/plunks/6UTb9kHRZ363Ivhh2BPE/"
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 0
.navigate url
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByName("points")(0).Value)
Range("A1").Value = myPoints
End Sub
Output:
CSS selector:
Use a CSS selector to get the element of input[name='points']
You don't show enough HTML to know if this is the only on the page. The above says elements with input tag having attribute name whose value is 'points'
CSS query:
VBA:
You apply the CSS selector with .querySelector method of document for a single element; .querySelectorAll for a nodeList of all matching elements i.e. if there is more than one on the page and you get the one of interest by index.
Debug.Print ie.document.querySelector("input[name='points']").getAttribute("value")
You need to use .getAttribute("name of attribute") to get an attributes value. In your case .getAttribute("value") will return 300.
Dim ie As Object
Dim myPoints As String
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 1
.navigate "website URL"
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByTagName("td")(0).getElementsByTagName("input")(0).getAttribute("value"))
Range("A1").Value = myPoints
Just on a side note. I don't know much about HTML and maybe someone can elaborate on this more. But if you want to test that HTML code you need to add in the < table> < tr> tags.
Something like this:
<table>
<tr>
<td id="myPower_val_9" style="visibility: visible;">
<input type="text" disabled="disabled" value="300" name="points"></input>
</td>
</tr>
</table>

HTML object library / pull

I have the following code in an HTML web page, and I am trying to use the html object library via vba engine to pull the value from within this tag:
<input name="txtAdd_Line1" disabled="disabled" size="30" maxLength="50" value="123 N 1ST ST"/>
I figure I have to use .getelementsbytagname or .getelementsbyname, but I am not sure how to grab the value. Does anyone have any ideas?
Here's an example with comments, subtitute in your actual address:
Sub Example()
'Declare needed variables
Dim ie, elements
Dim x As Long
'Create IE Applction
Set ie = CreateObject("InternetExplorer.Application")
'Navigate to the website
ie.navigate "C:\test.html" 'Substitute your actual address
'Wait for website to finish loading
Do While ie.ReadyState <> 4
Loop
'Find the elements
Set elements = ie.document.getelementsbyName("txtAdd_Line1")
'Display the value of each returned element
For x = 0 To elements.Length - 1
MsgBox elements(x).Value
Next
'Quit IE
ie.Quit
End Sub
Based on your comment most likely just looking at the document wasn't retrieving the actual layer of the tree you wanted, try this:
Set HTMLDoc = ie.document.frames("MainFrame").document
With HTMLDoc
'This returns an (object) which contains an array of all matching elements
a = .getElementsByName("txtAdd_Line1")
end with
For x = 0 to a.length
msgbox a(x).value
next
You can use a CSS selector of input[name='txtAdd_Line1'] . This says element with input tag having attribute name with value 'txtAdd_Line1'.
CSS selector:
You apply a CSS selector using the .querySelector method of document e.g.
Msgbox ie.document.querySelector("input[name='txtAdd_Line1']").innerText