I want to fill data with javascript in selenium vba - vba

I have a web form of pre populated data which i have to fill. I want to do this with javascript. I am not sure to write the code .
Public Sub ABC()
Dim document As HTMLDocument
Dim bot As New WebDriver
Dim cSCRIPT,m
m=58
bot.Start "chrome", ""
bot.Get "https://XYZ"
cSCRIPT = "document.getElementByXPath("//td[contains(text(),'XXXX')]/following-sibling::td[5]").value='" & m & "'"
bot.ExecuteScript cSCRIPT
But my code do not work. How can I code in selenium VBA with the help of XPath to fill data in Web table.

I can't test this in VBA but have translated from python which worked. I use evaluate to handle the xpath matching.
The function Document.evaluate() has the following definition:
var xpathResult = document.evaluate(
xpathExpression,
contextNode,
namespaceResolver,
resultType,
result
);
The first argument is the xpath expression. Then contextNode is basically the node to apply the xpath to i.e. document in this case. namespaceResolver is null as no namespace prefixes are used (it's an html document). I specify resultType as XPathResult.ANY_TYPE so as to get the natural type from the expression. For result argument I pass null so as to create a new XPathResult.
The return type is object. I use iterateNext to get to the input element and then assign the visual value with input.value = "abc"; and the actual with input.setAttribute('value', 'xyz');.
I am using a testable example but you would amend as per your html which means your xpath would be
//td[contains(text(),'XXXX')]/following-sibling::td[5]/input
VBA:
Dim s As String
s = "var td = document.evaluate(""//tr[contains(td/text(), 'Angelica Ramos')]/td[2]/input"", document, null, XPathResult.ANY_TYPE, null);" & _
"var input = td.iterateNext();" & _
"input.value = 'abc';" & _
"input.setAttribute('value', 'xyz');"
bot.Get 'https://datatables.net/examples/api/form.html'
bot.ExecuteScript s
Python original:
bot.get('https://datatables.net/examples/api/form.html')
s = '''var td = document.evaluate("//tr[contains(td/text(), 'Angelica Ramos')]/td[2]/input", document, null, XPathResult.ANY_TYPE, null);
var input = td.iterateNext();
input.value = 'abc';
input.setAttribute('value', 'xyz');
'''
bot.execute_script(s)
References:
https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate
https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate#Result_types
https://javascript.info/object
https://developer.mozilla.org/en-US/docs/Web/API/XPathResult/singleNodeValue
https://developer.mozilla.org/en-US/docs/Web/API/XPathResult/iterateNext

It's tough to debug the XPath without seeing the table and underlying HTML for the webpage, but typically if a table is tough to access, I just use a macro tool like AppRobotic to leverage screen X,Y coordinates of the table to click into it and fill in the necessary info, sometimes navigating using keyboard strokes while in the table works well also:
Set Waiter = CreateObject("Selenium.Waiter")
Set Assert = CreateObject("Selenium.Assert")
Set bot = CreateObject("Selenium.FirefoxDriver")
Set x = CreateObject("AppRobotic.API")
Sub ABC
' Open webpage
bot.Get "https://www.google.com"
' Wait a couple of seconds
x.Wait(2000)
' Type in the Search box
bot.FindElementByName("q").SendKeys "test"
bot.Keys.Return
' Or use coordinates if an element is difficult to find
' Use UI Item Explorer to get X,Y coordinates of Search box and move cursor
x.MoveCursor(438, 435)
' click inside Search box
x.MouseLeftClick
x.Type("test")
x.Type("{ENTER}")
While Waiter.Not(Instr(bot.Title, "test")): Wend
x.MessageBox("Click OK to quit the browser"):
bot.Quit
End Sub

Related

How to check the visibility of the element in Edge Browser using VBA Selenium and alternative to Obj.Wait

Unable to check the visibility of the element. Please guide
At present using Obj.Wait to wait so that Site is loaded fully. Is there any other way which can check the Edge Browser has loaded fully before execution of next code
In the instant case
Obj.Wait 580000
If Obj.IsElementPresent(By.XPath("//*[#id='downloadReport']/div")) = True Then
I have a following VBA code + Selenium which is opening a website, populate data from Excel and displays the record.
Dim Obj As New WebDriver
Sub EdgeAutoSF1CRA0()
Set Obj = New Selenium.EdgeDriver
Dim By As New Selenium.By
Obj.SetCapability "ms:edgeOptions", "{""excludeSwitches"":[""enable-automation""]}"
Obj.Start "edge", ""
Obj.Get "https://************
Obj.Window.Maximize
Obj.FindElementByName("croreAccount").SendKeys ("Search")
Obj.FindElementByXPath("//*[#id='loadSuitFiledDataSearchAction']/div[1]/div[3]/div[4]/img").Click
Obj.FindElementById("borrowerName").SendKeys (ThisWorkbook.Sheets("Sheet1").Range("C5").Value)
Obj.FindElementByXPath("//*[#id='search-button']/ul/li[1]/div/input").Click
Obj.Wait 580000
If Obj.IsElementPresent(By.XPath("//*[#id='downloadReport']/div")) = True Then
Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click
Else
Obj.FindElementByXPath("//*[#id='three-icons']/ul/li[3]/a/div").Click
End If
'Call EdgeAutoSF25LA0
End Sub
If match is found, then element
XPath("//*[#id='downloadReport']/div")
is visible. The code at site is
'a href="downloadStatusReport" id="downloadReport">Download</a
If no match is found, then element
XPath("//*[#id='downloadReport']/div")
is not visible. The code at site is
'a href="downloadStatusReport" id="downloadReport" style="display: none;">Download</a

Extract Data with Selenium and VBA

I'm new using selenium. I have a site that is not more compatible with the IE, so i decided to try this new technique, but can't see what is wrong on my code. Any help will be apreciated.
Sub ExtractPrice()
Dim bot As WebDriver, myproducts As WebElements, myproduct As WebElement
Set bot = New WebDriver
bot.Start "chrome"
bot.Get "https://www.veadigital.com.ar/prod/72060/lechuga-capuchina-por-kg"
' Application.Wait Now + TimeValue("00:00:20")
Set myproducts = bot.FindElementsByClass("datos-producto-container")
'
For Each myproduct In myproducts
If myproduct.FindElementByClass("product-price").Text <> "" Then
'Debug.Print myproducts.FindElementByClass("product-price").Text
Worksheets("VEA").Range("b2").Value = myproducts.FindElementsByClass("product-price").Text
End If
Next
MsgBox ("complete")
End Sub
Issue is in this line :
Worksheets("VEA").Range("b2").Value = myproducts.FindElementsByClass("product-price").Text
Remember FindElements, returns a list of webelements rather than webelement. Instaead use the line you have used in if condition.
Worksheets("VEA").Range("b2").Value=myproduct.FindElementByClass("product-price").Text
Note : With above line of code you will get your price, but it will come as $379 instead of $3.79. As there is no . in price on page. Better way to store price is :
Dim intValue = myproduct.FindElementByClass("product-price").Text
Dim decValue= myproduct.findElementByXPath(".//div[#class='product-price']//span").Text
Worksheets("VEA").Range("b2").Value = Replace(intValue , decValue, "."&decValue)
Above will assign $3.79.

Getting .value property when using a string and variable

I am creating a form in Access which will be used as an order sheet for classroom materials. I have the available resources listed and a text box next to the resource where the user inputs the quantity they desire.
My VBA code checks to see if any entries have been made by using the following. (I am using Nz() to allow for Null results):
QuantCheck = Nz(Box1.Value, 0) + Nz(Box2.Value, 0) + Nz(Box3.Value, 0)
Where "QuantCheck" is the variable I am using in the IF statement which begins the workflow:
If QuantCheck > 0 Then
I would like to clean this up by using some kind of loop statement, however I am not able to extract the .value from a string. I would love something like the following which I could incorporate into a loop:
"Box"&VariableNumber.Value
From what I can tell, I am not able to use a string (concatenated or otherwise) as the base for the .value call.
It is interesting that there is a way to accomplish this when using a SQL statement. I have this elsewhere in the code which works nicely:
SQLStr = "INSERT INTO OrderRequests VALUES (cbSchool, txtName, Title" & x & ".caption, Box" & x & ")"
Here I have a variable "x" which increases with each loop to change the Title line, and the Box line.
Any help is appreciated.
I suggest you use the Tag property of the controls. Put "QuantCheck" in the Tag property of any control you want to include. Then
Function QuantitiesExist(frm As Form) As Boolean
Dim Ctrl As Control
Const sQUANTCHK As String = "QuantCheck"
For Each Ctrl In frm.Controls
If Ctrl.Tag = sQUANTCHK Then
If Nz(Ctrl.Value) > 0 Then
QuantitiesExist = True
Exit For
End If
End If
Next Ctrl
End Function
Now you get self documenting code
If QuantitiesExist(Me) Then
And when you add/delete/change controls, you don't have to edit your code. Just set up new controls with the proper tags.
You could loop through the control on the for checking the names and then if it is the one you wanted take an action on it, is this what you was thinking of?
Dim Ctrl As Control
For Each Ctrl In Me.Controls
If Ctrl.Name = "TxtPath" Then ' "Box" & VariableNumber Then
MsgBox Ctrl.Value
End If
Next

Grab specific part of text from a local html file and use it as variable

I am making a small "home" application using VB. As the title says, I want to grab a part of text from a local html file and use it as variable, or put it in a textbox.
I have tried something like this...
Private Sub Open_Button_Click(sender As Object, e As EventArgs) Handles Open_Button.Click
Dim openFileDialog As New OpenFileDialog()
openFileDialog.CheckFileExists = True
openFileDialog.CheckPathExists = True
openFileDialog.FileName = ""
openFileDialog.Filter = "All|*.*"
openFileDialog.Multiselect = False
openFileDialog.Title = "Open"
If openFileDialog.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim fileReader As String = My.Computer.FileSystem.ReadAllText(openFileDialog1.FileName)
TextBox.Text = fileReader
End If
End Sub
The result is to load the whole html code inside this textbox. What should I do so to grab a specific part of html files's code? Let's say I want to grab only the word text from this span...<span id="something">This is a text!!!</a>
I make the following assumptions on this answer.
Your html is valid - i.e. the id is completely unique in the document.
You will always have an id on your html tag
You'll always be using the same tag (e.g. span)
I'd do something like this:
' get the html document
Dim fileReader As String = My.Computer.FileSystem.ReadAllText(openFileDialog1.FileName)
' split the html text based on the span element
Dim fileSplit as string() = fileReader.Split(New String () {"<span id=""something"">"}, StringSplitOptions.None)
' get the last part of the text
fileReader = fileSplit.last
' we now need to trim everything after the close tag
fileSplit = fileReader.Split(New String () {"</span>"}, StringSplitOptions.None)
' get the first part of the text
fileReader = fileSplit.first
' the fileReader variable should now contain the contents of the span tag with id "something"
Note: this code is untested and I've typed it on the stack exchange mobile app, so there might be some auto correct typos in it.
You might want to add in some error validation such as making sure that the span element only occurs once, etc.
Using an HTML parser is highly recommended due to the HTML language's many nested tags (see this question for example).
However, finding the contents of a single tag using Regex is possible with no bigger problems if the HTML is formatted correctly.
This would be what you need (the function is case-insensitive):
Public Function FindTextInSpan(ByVal HTML As String, ByVal SpanId As String, ByVal LookFor As String) As String
Dim m As Match = Regex.Match(HTML, "(?<=<span.+id=""" & SpanId & """.*>.*)" & LookFor & "(?=.*<\/span>)", RegexOptions.IgnoreCase)
Return If(m IsNot Nothing, m.Value, "")
End Function
The parameters of the function are:
HTML: The HTML code as string.
SpanId: The id of the span (ex. <span id="hello"> - hello is the id)
LookFor: What text to look for inside the span.
Online test: http://ideone.com/luGw1V

how to refer back to created browser instance

I create a browser like so, and manually navigate to the web page I need to be. I intend to automatically pull certain elements once I get to the page I need to be on via a seperate macro
Sub Test()
Set CAS = New SHDocVw.InternetExplorer ' create a browser
CAS.Visible = True ' make it visible
CAS.navigate "http://intraneturl"
Do Until CAS.readyState = 4
DoEvents
Loop
This works fine, then I do
Public Sub Gather
Set HTMLDoc2 = CAS.document.frames("top").document
Call Timer1
With HTMLDoc2
.getElementById("tab4").FirstChild.Click
End With
Call Timer2
Dim fir, las, add1, add2, cit, stat, zi As String
Dim First As Variant
Dim Last As Variant
Dim addr1 As Variant
Dim addr2 As Variant
Dim city As Variant
Dim Thisstate As Variant
Dim Zip As Variant
Call Timer2
Set HTMLDoc = CAS.document.frames("MainFrame").document
Call Timer2
With HTMLDoc
First = .getElementsByName("IndFirst")
Last = .getElementsByName("IndLast")
addr1 = .getElementsByName("txtAdd_Line1")
addr2 = .getElementsByName("txtAdd_Line2")
city = .getElementsByName("txtAdd_City")
Thisstate = .getElementsByName("cmb_Add_State")
Zip = .getElementsByName("txtAdd_Zip")
End With
fir = First.Value
las = Last.Value
add1 = addr1.Value
add2 = addr2.Value
cit = city.Value
stat = Thisstate.Value
zi = Zip.Value
'navigate back to start page
With HTMLDoc2
.getElementById("tab3").FirstChild.Click
End With
End Sub
This works the first time, but after the first time, I get "Object variable or with block variable not set" when trying to run the gather() sub again, on a different web page that contains similar information. Any Ideas as to what im doing wrong?
"The error "object variable or with block variable not set" occurs on: Set HTMLDoc2 = CAS.document.frames("top").document the second time i try running Gather()."
This is probably one of three things:
CAS is no longer an object
To check this, set a breakpoint on the line, press ctr+G in the VBA Editor and type ?CAS Is Nothing in the Immediate Window; the result should be False; if it is True CAS is no longer an object
Like Daniel Dusek suggested, make sure CAS.document.frames("top") is an actual element on the page.
To check this, open the webpage you are trying to script, press F12 in Internet Explorer, click on the arrow in the toolbar and click on the "top" frame element in the webpage, switch back to the Developer Tool and look at the line highlighted. Make sure the frame element is named "top".
The HTML hasn't fully loaded when you try to reference the frame element. Set a longer delay or a loop.
i.e. (untested):
Do Until HtmlDoc2 Is Nothing = false
Set HTMLDoc2 = CAS.document.frames("top").document
Loop
Maybe the more important question is why manually navigate to another page? Can't you automate that part of your process too?