I'm working in Microsoft Excel 2010 VBA and I'm trying to automate pdf report downloads. I can navigate to where I need to be but I'm stuck on the last step;
There's an "Export" element that behaves like a link. If I were to click on it, it would initiate a pdf download. I want to automate this and direct the download to a specified folder, but when I inspect the element it doesn't appear to have an element ID by which I can call it (ie. getElementById().Click).
I'm new to VBA IE automation. Does anyone have suggestions on how to initiate that "Export" function?
I can't share the full HTML detail, as it is for a site with sensitive data, but below are snippets of the HTML, my VBA code, and a screenshot of the HTML section that is in question:
<DIV style="FONT-SIZE: 8pt; HEIGHT: 30px; FONT-FAMILY: Verdana; DISPLAY: inline"><TABLE style="DISPLAY: inline" cellSpacing=0 cellPadding=0>
<TBODY>
<TR>
<TD height=28><SELECT id=PrintFormat><OPTION value="">Select a format</OPTION><OPTION selected value=Pdf>Acrobat (PDF) file</OPTION></SELECT></TD>
<TD width=4></TD>
<TD height=28><A onclick="if (document.getElementById('PrintFormat').value != '') {var url = document.getElementById('PrintFormat').value + 'Stream.aspx'; document.location.href = url;}" style="FONT-SIZE: 8pt; TEXT-DECORATION: none; FONT-FAMILY: Verdana" href="#" shape="">Export</A></TD></TR></TBODY></TABLE></DIV>
My vba code;
Sub pdfdownloadautomation()
Set objIE = New InternetExplorer
objIE.Visible = True
objIE.navigate Sheet3.Range("A1").Value 'A1 contains url
objIE.document.getElementById("PrintFormat").Value = "Pdf"
'works fine to this point - just not sure how to call the export
objIE.document.getElementById("?").Click
End Sub
screenshot of inspected element in IE
You may try a CSS selector as follows to target the element. I can't test and don't know if there are parent frame/iframe/form tags to navigate. There appears to be one form element at least visible. Please provide the html including this form tag if possible. I have given variations without parent form and including parent form (which looks to be the case) - the assumption with the parent form element is that it is the first form element on the page.
ie.document.querySelector("td a[onclick^='if']").Click
Which might become:
ie.document.getElementsByTagName("form")(0).querySelector("td a[onclick^='if']").Click
The CSS selector is likely more complex than required but I am trying to localize using the visible cues in your image i.e. that there is an element with an a tag, that has onclick attribute starting with 'if', and is inside a parent td tag.
You could also gather all the a tag elements and loop until the Export innerText is found:
Dim list As Object, item As Object
Set list = ie.document.getElementsByTagName("a")
For Each item In list
If item.innerText = "Export" Then
item.Click
Exit For
End If
Next
Again, this may become
Set list = ie.document.getElementsByTagName("form")(0).getElementsByTagName("a")
Related
I am trying to login on a company website to upload a file. I am successful on login process but I failed on selecting the "Text button" that expands the other selection.
[enter image description here][1]
Below is the HTML elements.
<div class="tree_margin" id="tree_root/14+" style="text-decoration: none; display: block;">
<a class="tree_hover" href="JavaScript:tree_switch_folder('tree_root/14+');">
<img src="skin/blue/sidemenu_minus.jpg" border="0" alt="資材システム" align="absmiddle"> 資材システム</a><br></div>
資材システム
I tried the code below but it gives me "error:438".
Set objIE = New InternetExplorerMedium
objIE.Document.getElementByClassId("tree_root/14+").Click
Thank you in advance for your help.
Assuming you have left a sufficient page load wait, and that you have shown the correct html, I believe you want to click the anchor tag. You can target its class via css class selector and the querySelector() method:
objIE.Document.querySelector(".tree_hover").click
Trying to automate an on-line order process. Once the order specification is completed you press the Save button. If the item specification (height) is >1000mm a popup is displayed. I am having problems automating Clicking it. It is not in an iframe.
On the second line of code below:
bot.FindElementById("ctl00_mainContent_ucOrderDetailBlind_btnSave").Click
'Clicking 'causes' the popup warning to display if the >1000mm condition is TRUE. It has only one button - 'OK' (coded as nbsp;OKnbsp; ). Clicking manually allows automatic progress to go to the next page but I need help to get the coding to do this automatically. At the moment the code appears to be hung displaying the popup. Debug indicates that the next instruction cannot be executed - obviously because that findElementById....Click cannot be found because the process has got stuck.
I have tried several Alternatives - please see the code for the most likely that I tried.
If ThisWorkbook.Sheets("SquareSpace").Cells(r, "F").Value > 1000 Then ' The item is more than 1000mm high
bot.FindElementById("ctl00_mainContent_ucOrderDetailBlind_btnSave").Click
'Alternative 1
bot.FindElementByXPath("//input[#ID='popup_ok' and text()=' OK ']").Click
'Alternative 2
bot.SwitchToAlert.Accept
bot.Wait 1000
Alternative 3
bot.FindElementById("popup_ok").Click
Else ' for when the item is not more than 1000mm high
bot.FindElementById("ctl00_mainContent_ucOrderDetailBlind_btnSave").Click
bot.Wait 500
End If
'Then the next instruction of the form bot.FindElementById ......Click
The HTML with some stuff removed for clarity (text and styling):
<div id="popup_container" class="ui-draggable" style="position: absolute; z-index: 99999; padding: 0px; margin: 0px; min-width: 604px; max-width: 604px; top: 0px; left: 647.5px;"><h1>Warning</h1>
<div id="popup_content" class="alert">
<div id="popup_message"><br>WARNING TEXT</div>
<div id="popup_panel"><input type="button" value=" OK " id="popup_ok"></div>
</div>
</div>
Any suggestions are appreciated. Thanks.
To click on the element with value as OK you can use either of the following Locator Strategies:
Using FindElementByCss():
bot.Wait 5000
bot.FindElementByCss("div#popup_container div#popup_panel > input#popup_ok").Click
Using FindElementByXPath():
bot.Wait 5000
bot.FindElementByXPath("//div[#id='popup_container']//div[#id='popup_panel']/input[#id='popup_ok']").Click
Try using javascript to click the button:
[edit - updated identifier]
element=bot.FindElementByXPath("//input[#id='popup_ok']")
x =bot.ExcuteScript("arguments[0].click();", element)
[code above updated - see comments]
[new below]
Had a bit of fun getting this to work as expected.
This is my little test function to give you an idea of what i used to check things:
Function selenium()
Dim driver As New WebDriver
Dim element As WebElement
driver.Start "chrome"
driver.Get "https://www.google.co.uk"
driver.FindElementByXPath("//input[#name='q']").SendKeys "hello world"
Set element = driver.FindElementByXPath("//input[#value='Google Search']")
x = driver.ExecuteScript("arguments[0].click();", element)
End Function
Not sure why i had to assign a response back from the execute script but it wouldn't work without it.
The best and simplest method I've found is to just use driver.SwitchToAlert(5).Accept. The 5 is just a number of seconds to wait before accepting the error message. You can use any number of seconds in here.
In Vb.net, with a Webbrowser, I normally use GetElementByIdto address for example a button. I know that there is GetElementFromPoint, which I find extremly laborious.
Is there a better, simpler way when the ID is unknown?
You will need to use some type of selector.
The GetElementByID method works best because if the HTML file is formatted correctly then there should only be one element with that unique ID.
The GetElementFromPoint will return an element based on the X,Y coordinate of the document, this is best used in the Document's Click event.
The GetElementByTagName name will return a collection of elements and works if you know the tag type of the element, such as <button>...</button> or <p>...</p>. To help narrow down which element you want, you will need to then iterate through the returned collection and compare either the element's attributes if you know their respective values or the element's text via the InnerHTML property.
The last and least effective method is the All property which returns every element in the document. The reason why this is the least effective is because at least with GetElementByTagName, you can narrow down the collection based on the tag's name.
However, let's assume that you have the following markup:
<body>
<p>Look at my super complex HTML markup.</p>
<button>Click Me</button>
<button>No, click me!</button>
</body>
You could then get the button tag that says "Click Me" by using the following:
Dim click_me As HtmlElement = WebBrowser1.Document.GetElementByTagName("button").SingleOrDefault(Function(e) e.InnerHtml = "Click Me")
Seeing as this question is asked every now and then I'll see if I can't try to tackle this once and for all. Here's a more extensive guide on how to find elements that don't have an ID:
- The basics -
There are plenty of built-in properties and methods you can use in order to identify an element. The most common ones include:
HtmlElement.GetElementsByTagName()
Method. Returns a collection of all elements in the document/element having the specified HTML tag. This can be called both on a HtmlElement but also on the HtmlDocument itself.
HtmlElement.GetAttribute()
Method. Returns the value of a specific attribute on the specified HtmlElement.
HtmlElement.InnerHtml
Property. Returns all HTML code located inside the specified element (but not the code for the element itself).
HtmlElement.InnerText
Property. Returns all text (stripped from HTML code) located inside the specified element.
HtmlElement.OuterHtml
Property. Returns the HTML code located inside the specified element, including the code for the element itself.
These methods and properties can all be used in different ways to identify an element, as illustrated by the examples below.
NOTE: I omitted HtmlElement.OuterText because its behaviour is a bit odd, and I'm not 100% sure what it actually does.
- Examples of finding elements with no ID -
Following are a set of examples of how you can use the previously mentioned methods and properties in order to find the element you're looking for.
Finding an element by its class(-name)
To find and element based on its class attribute you have to iterate all elements and check GetAttribute("className") on each. If you know the element type (tag name) beforehand you can narrow the search by first getting a collection of all the elements of that type using HtmlDocument.GetElementsByTagName() instead of HtmlDocument.All.
HTML code:
<div class="header">
<div id="title" class="centerHelper">
<img id="logo" src="img/logo.png"/>
</div>
<p class="headerContent">
Hello World!
</p>
</div>
Element to locate:
<p class="headerContent">
VB.NET code:
'Iterate all elements.
For Each Element As HtmlElement In WebBrowser1.Document.All
If Element.GetAttribute("className") = "headerContent" Then
'Found. Do something with 'Element'...
Exit For 'Stop looping.
End If
Next
Finding an element based on an attribute, located inside another element (with ID)
In order to find a child element based on one of its attributes, where the child is located inside a parent element (that has an ID) you simply need to get the parent element by its ID and then iterate all its children.
HTML code:
<select id="items" class="itemsList">
<option value="2">Apple</option>
<option value="3">Orange</option>
<option value="5">Banana</option>
</select>
Element to locate:
<option value="5">Banana</option>
VB.NET code:
'Iterate all children of the element with ID "items".
For Each Element As HtmlElement In WebBrowser1.Document.GetElementByID("items").Children
If Element.getAttribute("value") = "5" Then
'Found. Do something with 'Element'...
Exit For 'Stop looping.
End If
Next
Finding an element based on an attribute, located inside another element (without ID)
To find a child element based on one of its attributes, where the child is located inside a parent element (that doesn't have an ID) you first have to create an outer loop that looks for the parent element. Then, when found, you can start iterating the children.
HTML code:
<select class="itemsList">
<option value="2">Apple</option>
<option value="3">Orange</option>
<option value="5">Banana</option>
</select>
Element to locate:
<option value="5">Banana</option>
VB.NET code:
'Variable keeping track of whether we found the element we're looking for or not.
Dim ElementFound As Boolean = False
'Outer loop, looking for the parent object (<select class="itemsList">).
For Each Element As HtmlElement In WebBrowser1.Document.GetElementsByTagName("select") 'Iterate all <select> tags. You can use Document.All here as well.
If Element.getAttribute("className") = "itemsList" Then
'Parent found.
'Inner loop, looking for the child element we want (<option value="5">Banana</option>).
For Each OptionElement As HtmlElement In Element.GetElementsByTagName("option")
If OptionElement.GetAttribute("value") = "5" Then
'Found. Do something with 'OptionElement'...
ElementFound = True
Exit For 'Exit the inner loop.
End If
Next
'Exit the outer loop if we found the element we're looking for.
If ElementFound Then Exit For
End If
Next
Finding an element based on its InnerText
In some cases the element you want to locate doesn't have any attributes or is simply too similar to a lot of other elements on the site. In this case, if its contents are always the same you can identify it via its InnerText or InnerHtml properties.
HTML code:
<h1>Important information</h1>
<p>Please read this information through <b>carefully</b> before continuing.</p>
<h2>Copyrighted material<h2>
<p>All material (text, images, video, etc.) on this site are <b>copyrighted</b> to COMPANY NAME.</p>
Element to locate:
<h2>Copyrighted material<h2>
VB.NET code:
For Each Element As HtmlElement In WebBrowser.Document.All
If Element.InnerText = "Copyrighted material" Then
'Found. Do something with 'Element'...
Exit For 'Stop looping.
End If
Next
Finding an element based on its InnerHtml
Finding an element based on its InnerHtml works exactly the same way as when you look based on its InnerText apart from that the string you're checking now also includes HTML code.
HTML code:
<h1>Important information</h1>
<p>Please read this information through <b>carefully</b> before continuing.</p>
<h2>Copyrighted material<h2>
<p>All material (text, images, video, etc.) on this site are <b>copyrighted</b> to COMPANY NAME.</p>
Element to locate:
<p>All material (text, images, video, etc.) on this site are <b>copyrighted</b> to COMPANY NAME.</p>
VB.NET code:
'Iterate all <p> tags.
For Each Element As HtmlElement In WebBrowser.Document.GetElementsByTagName("p")
If Element.InnerHtml.Contains("<b>copyrighted</b>") Then
'Found. Do something with 'Element'...
Exit For 'Stop looping.
End If
Next
I cannot simulate a click event on a div element. The HTML code is this one:
<div id="export" data-export="true" data-timespan="">
<button class="btn btn-default ladda-button" type="button" data-style="expand-right" data-spinner-color="#8899a6">
<span class="Icon Icon--featherDownload"></span>
<span class="ladda-label">
Exportar datos
</span>
<span class="ladda-spinner"></span></button>
<div class="hidden error server Callout Callout--danger">
<p>Ocurrió un problema al exportar tus datos, inténtalo de nuevo más tarde.</p>
</div>
</div>
In the webpage, is just a button that downloads a file when clicked. There is no URL. But after checking the code, is the div element what triggers the click event. Look the image.
Click to see image
That "g.handle" triggers a js.file form the website.
My vba code works perfect for doing other stuff in the website, but I cannot accomplish this task.
I've tried all this options:
Set div = HTMLDoc.getElementById("export")
div.FireEvent ("onclick")
div.Click
HTMLDoc.all.Item
Call HTMLDoc.parentWindow.execScript("g.handle", "JavaScript")
Nothing works. I'm kind of blocked right now and I have no idea of how to proceed. Maybe this has no solution? I have almost no idea of HTML and I have 0 idea of Javascript. But it must have a solution, because if I do it manually, i click on that DIV, wait some seconds and download my file.
Thanks in advance. Any help is welcome.
You didn't specify if the highlighted portion was the html code for the button or not, but it appears from looking at it that it isn't.
You can grab the class name for your button. The name may or may not be unique, and it's difficult to know that without seeing the entire html code, but you can set the btn variable below to the first instance of that class name. If there are multiple class names in your html code, then you will need to use a For Each...Next statement to iterate through them (unless you know the instance number off hand).
So we will go with the first instance of your class, hence the reason for the (0) appended on the end.
Sub Test()
Dim ie As New InternetExplorer, HTMLDoc As HTMLDocument, btn As Object
ie.navigate Url 'whatever this may be
' Some code to wait for your page to fully load
Set HTMLDoc = ie.document
Set btn = HTMLDoc.getElementsByClassName("btn btn-default ladda-button")(0)
btn.Click
End Sub
Fyi: I'm using the selenium package for R, the selection code is equal to javascript or python so I'm asking a general selenium question.
I have a container which I have to make visible by a click, this works.
Then I try to select an element inside this container, I think I find it correctly but the click on the element only makes the popup window disappear.
Example code:
<div class="dateRanges" style="top: 275.313px; display: block;">
<a class="top dateOption CUSTOM" id="id32" href="javascript:;">
Benutzerdefinierter Zeitraum
</a>
<a class="dateOption TODAY" id="id33" href="javascript:;">
Heute
</a>
...
</div>
Element I try to find is "top dateOption CUSTOM".
My different tries which all failed:
remDr$findElement(using = 'xpath','//a[contains(#class,"top")]')$clickElement()
remDr$findElement(value = '//a[#class = "top dateOption CUSTOM"]')$clickElement()
remDr$findElements(using = 'css selector','a[class="top dateOption CUSTOM"]')[[1]]$clickElement()
I don't get any error message, the click seems to happen as the popup disappears, but the effect of the button does not.
I tried to save the object, wait a few seconds and click afterwards with no different effect.
I also tried different approaches with "idc4" with the same outcome.
Would appreciate any help, thank you.