I have looked across everywhere to find answers but could not get it, so would like your help in guiding me.
Selenium Basic (Excel VBA ) is giving me syntax error at the time of compilation.
My Objective is to move the mouse to a known element.
Here are the codes
Dim driver As New WebDriver
Dim actions As Selenium.actions
driver.Start "chrome", "http://anywebsite.com"
driver.get "/"
WebElement we = driver.FindElementByClass("MyClassName")
actions.MoveToElement (we)
It gives me an error at the compilation level as and FindElementByClass is highlighted. However in the watch window I can see values against it.
Compile Error: Sub or Function Not Defined
Would request if you can guide me since in the watch
Change
driver.actions.MoveToElement(we)
to
driver.actions.MoveToElement(we).perform
Related
I'm trying to access a search text box inside of our company's ERP system using Selenium. The screenshot shows the text box and the Xpath of the element.
This is a little tricky, because that Menu Search pop-up isn't really a pop-up. It somehow shows up when a user types Control + M.
By installing ChroPath and testing I've found the text-box always starts with the following string:
txtMenuSearch_Namespace_
I've tried to imitate what's described here, here and here with no luck.
The latest attempt in the snippet of my code looks like this:
menu_search_input_box_elements = driver.find_elements_by_xpath("//*[contains(#id, ‘txtMenuSearch_Namespace_’)]")
for item in menu_search_input_box_elements:
print(item)
I get the following error message:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//*[contains(#id, ‘txtMenuSearch_Namespace_’)]' is not a valid XPath expression.
In all my attempts to get the syntax right I keep getting this message. Any help in figuring out how to .send_keys() to this field is greatly appreciated.
You get en error because of the ‘’ quotes, replace them with correct ones.
# id starts with txtMenuSearch_Namespace_
menu_search_input_box_elements = driver.find_elements_css_selector("[id^='txtMenuSearch_Namespace_']")
menu_search_input_box_elements = driver.find_elements_by_xpath("//*[contains(#id, 'txtMenuSearch_Namespace_')]")
I am trying to Debug.Print a value in the immediate window of an input text field
so when found that this line can be executed in the console
document.querySelector("#txtCaptcha").value
I thought it may be useful for me if I could get that value into the immediate window
I have tried such line but returned nothing in the immediate window (no error occurred but nothing in the immediate window)
Debug.Print .ExecuteScript("document.querySelector('#txtCaptcha').value;")
Simply I am searching for a way to be able to get the value from javascript command. I have no idea about javascript so I am stuck
You are mixing HTMLDocument and Selenium examples in the above.
You cannot return text in either scenario from ExecuteScript (or execScript for IE) direct. Use your script to write the value to an existing node (or create a new one) and then read from that via DOM parser.
Dim s As String
s = "captcha = document.querySelector('#txtCaptcha').value;" & _
"document.title = captcha;"
.ExecuteScript s
.FindElementByTag("Title").Text
But if FindElementById("txtCaptcha").Attribute("value") doesn't return the value I would be surprised if using javascript will. Though testing with javascript in browser using url from a prior question of yours does return the value.
I've been hitting my head on a brick wall with the following lovely conundrum:
The point is to get data from Excel to Google Sheets without uploading files, but going through the URLs. The following handily explain how all that business transpires:
https://www.youtube.com/watch?v=mX2_XNYPGiI
http://ramblings.mcpher.com/Home/excelquirks/exceldocsintegration/excelsheetsv4
I've tried to adapt the following code for the VBA side of things, common to both examples:
Option Explicit
Sub GetDataFromGoogle()
'link to tutorial: https://www.youtube.com/watch?v=mX2_XNYPGiI
Dim link As String
link = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSes7Jb06JRy8KSFyNlpUSzNQxSB_HZay4S8AB2IqzpZP0QdwGO5PFSS-6uzd8v_GsjlkXM31pby2jE/pubhtml"
Sheet4.QueryTables(1).Connection = "URL;" & link
Sheet4.QueryTables(1).Refresh False
Sheet4.Columns(1).ColumnWidth = 10
End Sub
Sub PushDataToGoogle()
Dim link As String
link = "https://docs.google.com/forms/d/e/1FAIpQLSeDHEKJmRDynwOAS4g53T9AVMtpXQkWsRGbAzLpLI7rdsbiFA/formResponse?entry.1155739640=4&fvv=1&draftResponse=%5B%5B%5Bnull%2C1155739640%2C%5B%224%22%5D%0D%0A%2C0%5D%0D%0A%5D%0D%0A%2Cnull%2C%2276341394568976993%22%5D%0D"
'go to that link, refresh it and ensure that the first column isn't too narrow
sheets("Sheet4").QueryTables(1).Connection = "URL;" & link
sheets("Sheet4").QueryTables(1).Refresh True
sheets("Sheet4").Columns(1).ColumnWidth = 10
End Sub
On the Google Sheets side, I used a Form to create a spreadsheet that is both the data source for an Excel import and the destination for the export of data. Replcaing POST and GET in the HTML of the form page is the way to get the destination URL. Subsequently, that URL can be either hardcoded or edited with variables to get various inputs for the "entry.######" part of the URL, and then they are passed on to the sheet in the next available line, which would suit my purposes.
I hit a snag(s) with the oAuth2 part of the operation. From the example on the Desktop Liberation site (second link above), credentials are created from Google and then inserted into a code that authenticates the connection before transmitting the data. As such:
Private Function sheetsOnceOff()
getGoogled "sheets", , _
"1023445954023-hq8gkdcmo9sue822d23gy9ak5hmun27.apps.googleusercontent.com", _
"dX7ABCDEGFBETFWtvX5ShmDfrgrQ"
End Function
If that isn't done, the prior lines of code will just return the main Google login page. I've made the credentials, determined the destination key for the spreadsheet, but I get an error in the getGoogled routine that says:
Runtime-error: '-2147024809 (80070057)': The parameter is incorrect."
parameter values are as follows:
- scope = 'sheets', as it should be
- replacement package = ""
- clientID is OK
- clientSecret is OK
- complain = true
- cloneFromScope = ""
- apikey =""
I'm thinking the replacement package should not be empty if complain is true, but I feel that I'm out of my depth here. At least the logic of what needs to happen makes sense to me, and I've managed to get the transfer from Google Sheets to work fine, but I'm just not sure how to handle the oAuth2 authentication matter.
Thank you in advance.
The following is by no means a complete solution, but it is a rudimentary start. The condition on the following is to be logged into gmail, and it will move one piece of data into Google sheets with its timestamp. It works by direclty opening the page that appears after a user clicks Submit on the form, thus imitating a submission. Link2 opens the spreadsheet where the response is passed.
The downside is that it requires the user to be logged into gmail already and will open a new tab each time a variable is passed, so avoid modifying it with arrays or loops. However, I will try putting together a prior vba gmail project with this to see if can work. More will be forthcoming, but for a hacked together band-aid solution, this will do if someone needs it.
Credits and info here:
[https://stackoverflow.com/questions/3166265/open-an-html-page-in-default-browser-with-vba
https://stackoverflow.com/questions/5915325/open-google-chrome-from-vba-excel
Sub PushDataToGoogle()
Dim chromePath As String
chromePath = """C:\Program Files\Google\Chrome\Application\chrome.exe"""
Dim link As String, link2 As String
link = "https://docs.google.com/forms/d/e/1FAIpQLSeDHEKJmRDynwOAS4gtpXQkWsRGbAzLpLI7rdsbiFA/formResponse?entry.1155739640=4&fvv=1&draftResponse=%5B%5B%5Bnull%2C11557396%224%22%5D%0D%0A%2C0%5D%0D%0A%5D%0D%0A%2Cnull%2C%227634134997568976993%22%5D%0D"
link2 = "https://docs.google.com/spreadsheets/d/1W8A3UuFQTeEwk6-hqERvuIMT_HInySZTNBOIs/edit#gid=11262360"
Shell (chromePath & link)
ThisWorkbook.FollowHyperlink link2
End Sub
Turns out, the solution is considerably simpler, thanks to the Selenium Basic library, available here, with a lot of useful info about how it works:
https://codingislove.com/browser-automation-in-excel-selenium/
A note for your attention: this download needs Selenium GoogleChrome driver version 2.33, the current latest version, to resolve an error with Chrome starting up properly - at least in my case, I need Chrome and not IE working.
In any case, Selenium is a god-send for those in a similar situation. When you download and install Selenium, go into Tools --> References in the VB Editor, and enable the Selenium Type Library.
The following code will do the job:
Option Explicit
Dim myHTML_Element As IHTMLElement
Dim Driver As New WebDriver
Sub seleniumtutorial()
Dim pword As String
Dim link As String, link2 As String
pword = ThisWorkbook.sheets("Sheet1").Range("D10")
Driver.Start "chrome", "https://gmail.com"
Driver.Get ("https://gmail.com")
Driver.FindElementById("identifierId").SendKeys "user#company.com"
Driver.FindElementById("identifierNext").Click
'Driver.Get ("https://sso.diversey.com/nidp/saml2/sso?id=DIVAuthContract30&sid=0&option=credential&sid=0")
Driver.FindElementById("uname").SendKeys "Username"
Driver.FindElementById("pass").SendKeys pword
Driver.FindElementByName("loginButton2").Click
link = "https://docs.google.com/forms/d/e/1FAIpQLSeDHEKJmRDyMt7rdsbiFA/formResponse?entry.1155739640=7&fvv=1&draftR=%5B%5B%5Bnull%2C115B%224%22%5D%0D%0A%2C0%5D%%0A%5D%0D%0A%2Cnull%2C%227634134997568976993%22%5D%0D"
link2 = "https://docs.google.com/spreadsheets/d/1W8A3UuyeEwk6-hqERvuIMT_HInySBOIs/edit#gid=1126962360"
Driver.Get (link)
Driver.Get (link2)
End Sub
link and link2 are, respectively, the page that confirms a response has been submitted and link2 is the link to the spreadsheet that has the list of responses. You can divide this routine into two subs - one logs in the user, and the other creates an array of the items to be fed to google, at which points it just refreshes the same page with the new values, as it cycles through the loop or array.
One more link:
Selenium VBA - exit sub without close browser window
Making the variables public ensures that the browser window doesn't close at the end of the session, or you can declare them inside the first sub if you want it to close. As for credentials, you can either hardcode them or feed them from the spreadsheet, but that's a technicality. Beyond that, it seems like a pretty straightforward routine and it does the job really well.
The syntax of the Selenium library is decidedly more modern than VBA and I'd say quite a bit more powerful
I am navigating a web application that will often throw an error if there is an attempt to click an element before it can be interacted with.
When using Selenium WebDriver (java), I can easily work around the problem:
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.elementToBeClickable(By.id("element")));
However, I am trying to write the script in VBA utilizing the Selenium type library, and, despite trying numerous different ways, the only success I am having is:
webdriver.wait
which I have been told should be avoided if at all possible. If someone can advise how to translate my java into VBA, or provide any other solution, I would be extremely grateful.
You might try looping until the element has been set correctly with a time out to ensure you can't go into an infinite loop. The danger with the accepted answer is there is no way to escape the loop if not found.
Dim t As Date, ele As Object
t = Timer
Do
DoEvents
On Error Resume Next
Set ele = .FindElementById("element")
On Error GoTo 0
If Timer - t = 10 Then Exit Do '<==To avoid infinite loop
Loop While ele Is Nothing
Note: User #florentbr wrote a js wait clickable function to be executed via Selenium Basic. Example in this SO answer.
The selenium plugin for VBA is unofficial and doesn't support this feature.
You can work around this by using onError to retry the action that is producing an error until it succeeds or times out:
Sub test
OnError GoTo Retry
webDriver.findElementById("element")
Exit Sub
Dim i as integer
:Retry
webDriver.Wait(500)
i = i + 1
if i = 20 then onerror go to 0
Resume
end sub
In vba you can use Implicit wait "driver.Timeouts.ImplicitWait = 10 'Timeunits 'seconds" it will wait maximum limit if the element is found before the set time it will process further.
You can use a variation in the old wait system used with Internet Explorer.
Do While SeDriver.FindElementById("Id").IsDisplayed = False
DoEvents
Loop
Just replace "IsDisplayed" with "IsPresent", as needed. This method introduces an artificial implicit wait, perfect if what you need is to wait for an element generated by an AJAX request.
Often there are elements on-site that can be immediately found by selenium but cannot be interacted with and usually when we debug at this stage and run the code from there, everything works just fine. The solution I used for that was adding wait.
Sub Pause_for_2_seconds()
Application.Wait (Now + TimeValue("00:00:02"))
End Sub
And Where ever you feel that the element require some time just add
Call Pause_for_2_seconds
in your actual Sub
For Example see below
chr.SendKeys (Keys.Tab)
Call Pause_for_2_seconds
chr.SendKeys (Keys.Tab)
chr.SendKeys (Date + 52)
chr.SendKeys (Keys.Tab)
Call Pause_for_2_seconds
chr.SendKeys (Keys.Tab)
chr.SendKeys (Keys.Enter)
In order to head off a storm of "comment it out" replies, here is my situation:
I have a process is normally run 1 iteration by 1 iteration. A user manually hits a button that calls a macro which, upon completion, pops up a message box that reports the total time length the macro ran for. It's pretty handy for diagnosing issues. This code is locked down and I cannot modify it.
I am trying to do this at scale. Because the code in the main spreadsheet and workbook are locked, I have a separate workbook open in the same instance of excel with a macro that operates the locked down workbook. Rather than 1 by 1, I've got a set of 300 I'm trying to run through. Right now I have to babysit the thing and hit space to get past the MsgBox. Does anyone know of any tricks to prevent me having to monitor the thing? Either disabling the pop-ups or some way to make them non-modal. Maybe a trick to make the mouse click?
You're right in knowing that the best way to fix the issue is to correct the code. In which case you would probably make the pop-ups toggle-able.
However, I wrote this for you which could be used as a potential work around. It utilizes VBScript to "sort-of" simulate multithreading so that you can send a key to the modal Msgbox. Assuming you can do what you want to do via code, simply call SendDelayedKeys before the action that will cause a Msgbox. You may have to tinker with the Delay based upon your circumstances as 100 milliseconds may not be enough. To change the Delay, just call like this: SendDelayedKeys 500 for 500 milliseconds.
Sub SendDelayedKeys(Optional Delay As Long = 100, Optional keys As String = """ """)
Dim oFSO As Object
Dim oFile As Object
Dim sFile As String
sFile = "C:\SendKeys.vbs" 'Make this a valid path to which you can write.
'Check for the .vbs file.
If Not Len(Dir$(sFile)) Then
'Create the vbs file.
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.CreateTextFile(sFile)
oFile.WriteLine "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
oFile.WriteLine "WScript.Sleep CLng(WScript.Arguments(0))"
oFile.WriteLine "WshShell.SendKeys WScript.Arguments(1)"
oFile.Close
End If
Shell "wscript C:\SendKeys.vbs " & Delay & " " & keys
End Sub
Sub ProofOfConcept()
'Using default parameters which sends a space after 100 milliseconds
SendDelayedKeys
MsgBox "I disappear on my own!"
End Sub
A word of warning: Any solution that utilizes SendKeys is a fragile solution and should be avoided when possible. However, when your options are limited and you need to avoid a manual process, sometimes it's your only option.
Since SiddhartRout rightly pointed out that this could be solved using API calls: here's a link with C# code that would close your msgbox every second.
The problem here really isn't strictly a problem more code can (or indeed should) solve.
There are a great many things to consider and any solution will be more complex AND less reliable than the problem it is initially trying to solve. But lets look at your options...
SendKeys is not reliable for that kind of use, what happens if the dialogue says "would you like me to save this workbook?" just after making a change that was meant to be temporary or "would you like to play global thermonuclear war?" Plus with a batch process like that you want to get on with something else while you wait, even if it's only to come here to downvote trolls. If nothing else you may not be in control of this code so what kind of mess will it cause when the maintainers realise msgbox is bad UX and kill it?
FindWindow API calls would let you check the content in the window to make sure it says what you're expecting but then you're potentially asking some bit of quick & dirty vbscript to go into a race condition until the right window comes up. Can you guarantee that the threads won't lock up?. What about platform issues - what happens if someone wants to run your code on their shiny new surface? What happens when your 64 bit modal Excel dialogue window can't be seen by the 32-bit api calls you were making? What about a new version of office that doesn't present modal dialogues in the same way? None of those problems are insurmountable but each adds complexity (aka opportunity for failure.)
The best solution is fix the actual problem you have identified from the outset which is that the original code throws up an unnecessary modal dialogue. Someone needs to fix that - it doesn't have to be you but if you estimate how much time that modal dialogue wastes in lost productivity that should get you a solid business case for getting it sorted.