InvalidSelector Error: compound class names not permitted using "FindElementByClass" - vba

I am having trouble with code while trying to take the class name. I have tried and run time error 32 is appearing as:
InvalidSelector Error: compund class names not permitted
Maybe somebody canhelp with the code below. I have chrome browser 103.5060.114 but web driver 103.5060.56 . I could not find other driver to wnload.
HTML:
<span dir="auto" title="Customer" class="ggj6brxn gfz4du6o r7fjleex g0rxnol2 lhj4utae le5p0ye3 l7jjieqr i0jNr">Customer</span>
VBA code:
Sub iselementpresenttest()
Dim bot As New WebDriver
Dim ks As New Keys
'Init New Chrome instance & navigate to WebWhatsApp
bot.Start "chrome", "https://web.whatsapp.com/"
bot.Get "/"
bot.Window.Maximize
MsgBox "Please scan the QR code. After you are logged in, please confirm this message box by clicking 'ok'"
bot.Wait (3500)
' If bot.FindElementByClass("_2qo4q _3NIfV") = 1 Then
' Debug.Print "true"
'End If
If bot.FindElementByClass("ggj6brxn gfz4du6o r7fjleex g0rxnol2 lhj4utae le5p0ye3 l7jjieqr i0jNr") = 1 Then
' If bot.FindElementByXPath("//*[#id='pane-side']/div/div/div/div[11]/div/div/div[2]/div[2]/div[1]/span/div/span") = 1 Then
Debug.Print "true"
End If
' If bot.FindElementsByXPath("//*[#id='main']/div[3]/div/div[2]/div[3]/div[20]/div/div[1]/div[1]/div[2]/div/div/span").Count > 0 Then
If bot.FindElementsByXPath("//*[#id='main']/div[3]/div/div[2]/div[3]/div[20]/div/div[1]/div[1]/div[2]/div/div/span").Count > 0 Then
bot.TakeScreenshot.ToExcel.PasteSpecial (xlPasteAll)
bot.Quit
MsgBox "Yes"
Else
bot.Quit
MsgBox "NO"
End If
End Sub
My aim is to take the send receive whatsapp icon to the excel sheet

You need to take care of a couple of things here:
The classnames of the <span> looks dynamic and and may change sooner or later, even may be next time you access the application afresh.
Selenium doesn't permit compund class names
Solution
To identify the element you can use the following locator strategies:
Using FindElementByCss:
bot.FindElementByCss("span[title='Customer']")
Using FindElementByXPath:
bot.FindElementByXPath("//span[#title='Customer' and text()='Customer']")

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

Run time error '424' Object Required in Selenium VBA for Edge Browser

On executing below code, system throws error
Run Time Error '424'
Object Required
Through the code, I am extracting data from a website (using Edge browser).
If match is found, a link appears on the portal from which data can be downloaded whose Xpath is "//*[#id='downloadReport']/div"
If link is present, the link should be clicked using code
Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click
Else if no match is found, the above code should be skipped as Xpath as above is not available and another macro execution should be called using
Call EdgeAutoTest2
I am new to coding
Please help me in resolving this error.
Sub EdgeAutoTest1()
Set Obj = New selenium.EdgeDriver
Obj.SetCapability "ms:edgeOptions", "{""excludeSwitches"":[""enable-automation""]}"
Obj.Start "edge", ""
Obj.Get "https://***website*****"
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 20000
Obj.FindElementByXPath("//*[#id='three-icons']/ul/li[3]/a/div").Click
Obj.Wait 20000
If Obj.IsElementPresent(By.XPath("//*[#id='downloadReport']/div")) = True Then **'error occurs here*****
Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click
Else
Obj.Wait 100
End If
Call EdgeAutoTest2
End Sub
The only issue in your code is you are missing is that you didn't define By.
As you are using:
If Obj.IsElementPresent(By.XPath("//*[#id='downloadReport']/div")) = True Then
You need to:
Dim By As New Selenium.By

Trouble clicking on a load more button using vba

I'm trying to click on the Load More button located at the bottom of the left window of this webpage using vba in combination with selenium but the script always throws timeout error pointing at this .Get Url line. Although it seems I've defined an accurate xpath to locate the element, I can't think further as to what I should do now to achieve the same.
How can I click on that Load More button?
Sub ClickOnLoadMore()
Const Url$ = "http://www.ratemyprofessors.com/search.jsp?queryoption=TEACHER&queryBy=schoolDetails&schoolID=457&schoolName=James+Madison+University&dept=select"
Dim driver As New ChromeDriver, post As Object
With driver
.Get Url
Set post = .FindElementByXPath("//div[contains(.,'Load More')]")
.ExecuteScript "arguments[0].scrollIntoView();", post
post.Click
End With
End Sub
I see two "Load More" buttons. Both are matched by "//div[contains(.,'Load More')]". The first one is hidden. You need to handle second one.
Try this XPath
"//div[#class='content' and . = 'Load More']"
At least for me there were a couple of banners to dismiss as well as scrolling. There was no problem with the get line
Option Explicit
Public Sub ClickOnLoadMore()
Const Url$ = "http://www.ratemyprofessors.com/search.jsp?queryoption=TEACHER&queryBy=schoolDetails&schoolID=457&schoolName=James+Madison+University&dept=select"
Dim driver As New ChromeDriver, post As Object
With driver
.get Url
If .FindElementsByCss(".close-notice.close-this").Count > 0 Then
.FindElementByCss(".close-notice.close-this").Click
End If
.SwitchToFrame .FindElementByCss("[id^='spout-unit-iframe']")
With .FindElementByCss("#spout-ads #spout-header-close")
.ScrollIntoView
.Click
End With
.SwitchToDefaultContent
.ExecuteScript "document.querySelector('.result-list [onclick*=LoadMore]').scrollIntoView(true);" & _
"window.scrollBy(0, -(window.innerHeight - this.clientHeight) / 2);"
.FindElementByCss(".result-list [onclick*=LoadMore]").Click
Stop '<== Delete me later
'other code
.Quit
End With
End Sub

How to click the date picker selenium VBA?

I'd like to use selenium VBA to download some data from Yahoo Finance KOSPI COmposite Index.
I got the difficulty when click the date picker arrow to get the mini window to select the end date as today. I tried to record the marco through selenium IDE in chrome, but IDE does not record the step when I click the arrow of the Time period to get the date picker visible.
Below is my code in VBA.
Public Function seleniumKorea(bot As WebDriver)
Dim url As String
url = "https://finance.yahoo.com/quote/%5EKS11/history?period1=1484018309&period2=1515554309&interval=1d&filter=history&frequency=1d"
bot.Start "chrome", url
bot.Get "/"
'Not sure how to add date picker here
bot.FindElementByName("endDate").Clear
bot.FindElementByName("endDate").SendKeys (Date)
bot.FindElementByXPath("(.//*[normalize-space(text()) and normalize-space(.)='End Date'])[1]/following::button[1]").Click
Application.Wait (Now + TimeValue("0:01:00"))
bot.FindElementByXPath("(.//*[normalize-space(text()) and normalize-space(.)='As of'])[1]/following::div[4]").Click
Application.Wait (Now + TimeValue("0:01:00"))
bot.FindElementByXPath("(.//*[normalize-space(text()) and normalize-space(.)='Currency in KRW'])[1]/following::span[2]").Click
Application.Wait (Now + TimeValue("0:01:00"))
End Function
I tried to use the ByXPath to get the svg class but failed.
Thanks in advance.
I would use the following which submits the OATH consent if required and scrolls the date picker into the viewport
Option Explicit
Public Sub DatePicking()
Dim d As WebDriver
Set d = New ChromeDriver
Const URL = "https://finance.yahoo.com/quote/%5EKS11/history?period1=1484018309&period2=1515554309&interval=1d&filter=history&frequency=1d/"
With d
.get URL
If .Title = "Yahoo is now part of Oath" Then
.FindElementByCss("form").submit
End If
With .FindElementByCss("[data-test='date-picker-full-range']")
.ScrollIntoView
.Click
End With
With .FindElementByCss("[name=startDate]")
.Clear
.SendKeys "05/10/2017"
End With
With .FindElementByCss("[name=endDate]")
.Clear
.SendKeys "05/10/2017"
End With
Stop '<==Delete me later
.Quit
End With
End Sub
Check this out. It should serve the purpose. I used xpath to solve the issue.
Sub CustomizeDate()
Const Url$ = "https://finance.yahoo.com/quote/%5EKS11/history?period1=1484018309&period2=1515554309&interval=1d&filter=history&frequency=1d"
Dim driver As New ChromeDriver
With driver
.get Url
.FindElementByXPath("//input[#data-test='date-picker-full-range']", timeout:=5000).Click
.FindElementByXPath("//input[#name='startDate']").Clear.SendKeys ("01/05/2017")
.FindElementByXPath("//input[#name='endDate']").Clear.SendKeys ("01/08/2017")
.FindElementByXPath("//button/span[.='Done']", timeout:=5000).Click
.FindElementByXPath("//button/span[.='Apply']", timeout:=5000).Click
End With
End Sub
Don't use hardcoded delay for any item to appear. Use Explicit Wait instead. The script will wait upto 5000, meaning 5 seconds for that item to be available.

Loop over a set of pages with Selenium Basic (VBA)

Task:
So my first foray into Selenium and I am attempting to:
Find the number of pages in a pagination set listed at the bottom of https://codingislove.com/ This is purely to support task 2 by determining the loop end.
Loop over them
I believe these are linked but for those that want a single issue. I simply want to find the correct collection and loop over it to load each page.
The number of pages is, at time of writing, 6 as seen at the bottom of the webpage and shown below:
As an MCVE I simply want to find the number of pages and click my way through them. Using Selenium Basic.
What I have tried:
I have read through a number of online resources, I have listed but a few in references.
Task 1)
It seems that I should be able to find the count of pages using the Size property. But I can't seem to find the right object to use this with. I have made a number of attempts; a few shown below:
bot.FindElementsByXPath("//*[#id=""main""]/nav/div/a[3]").Size '<==this I think is too specific
bot.FindElementsByClass("page-numbers").Size
But these yield the run-time error 438:
"Object does not support this property or method"
And the following doesn't seem to expose the required methods:
bot.FindElementByCss(".navigation.pagination")
I have fudged with
bot.FindElementsByClass("page-numbers").Count + 1
But would like something more robust
Task 2)
I know that I can navigate to the next page, from page 1, with:
bot.FindElementByXPath("//*[#id=""main""]/nav/div/a[3]").Click
But I can't use this in a loop presumably because the XPath needs to be updated.
If not updated it leads to a runtime error 13.
As the re-directs follow a general pattern of
href="https://codingislove.com/page/pageNumber/"
I can again fudge my way through by constructing each URL in the loop with
bot.Get "https://codingislove.com/page/" & i & "/"
But I would like something more robust.
Question:
How do I loop over the pagination set in a robust fashion using selenium? Sure I am having a dense day and that there should be an easy to target appropriate collection to loop over.
Code - My current attempt
Option Explicit
Public Sub scrapeCIL()
Dim bot As New WebDriver, i As Long, pageCount As Long
bot.Start "chrome", "https://codingislove.com"
bot.Get "/"
pageCount = bot.FindElementsByClass("page-numbers").Count + 1 '
For i = 1 To pageCount 'technically can loop from 2 I know!
' bot.FindElementByXPath("//*[#id=""main""]/nav/div/a[3]").Click 'runtime error 13
' bot.FindElementByXPath("//*[#id=""main""]/nav/div/a[2]/span").Click ''runtime error 13
bot.Get "https://codingislove.com/page/" & i & "/"
Next i
Stop
bot.Quit
End Sub
Note:
Any supported browser will do. It doesn't have to be Chrome.
References:
Finding the number of pagination buttons in Selenium WebDriver
http://seleniumhome.blogspot.co.uk/2013/07/how-can-we-automate-pagination-using.html
Requirements:
Selenium Basic
ChromeDriver 2.37 'Or use IE but zoom must be at 100%
VBE Tools > references > Selenium type library
To click the element, it must be visible in the screen, so you need to scroll to the bottom of the page first (selenium might do this implicitly some times, but I don't find it reliable).
Try this:
Option Explicit
Public Sub scrapeCIL()
Dim bot As New WebDriver, btn As Object, i As Long, pageCount As Long
bot.Start "chrome", "https://codingislove.com"
bot.Get "/"
pageCount = bot.FindElementsByClass("page-numbers").Count
For i = 1 To pageCount
bot.ExecuteScript ("window.scrollTo(0,document.body.scrollHeight);")
Application.wait Now + TimeValue("00:00:02")
On Error Resume Next
Set btn = bot.FindElementByCss("a[class='next page-numbers']")
If btn.IsPresent = True Then
btn.Click
End If
On Error GoTo 0
Next i
bot.Quit
End Sub
Similar principle:
Option Explicit
Public Sub GetItems()
Dim i As Long
With New ChromeDriver
.Get "https://codingislove.com/"
For i = 1 To 6
.FindElementByXPath("//*[#id=""main""]/nav/div/a[3]").SendKeys ("Keys.PageDown")
Application.Wait Now + TimeValue("00:00:02")
On Error Resume Next
.FindElementByCss("a.next").Click
On Error GoTo 0
Next i
End With
End Sub
Reference:
'http://seleniumhome.blogspot.co.uk/2013/07/how-to-press-keyboard-in-selenium.html
If you're only interested in clicking through each of the pages (and getting the number of pages is just an aid to doing this) then you should be able to click this element until it's no longer there:
<span class="screen-reader-text">Next Page</span>
Using
bot.FindElementByXpath("//span[contains(text(), 'Next Page')]")
Have a loop click that link on each page load. Eventually it wont be there. Then use VBA's error/exception handling to handle whatever the equivalent of NoSuchElementException is in this implementation of WebDriver. You will need to re-find the element each time in the loop.
How about trying like this? Few days back I could figure out that there is an option .SendKeys("keys.END") which will lead you to the bottom of a page so that the driver can reach out the expected element to click. I used If Err.Number <> 0 Then Exit Do within the do loop so that if the scraper encounters any error, it will break out of loop as in, element not found error in this case when the clicking on the last page button is done.
Give this a shot:
Sub GetItems()
Dim pagenum As Object
With New ChromeDriver
.get "https://codingislove.com/"
Do
On Error Resume Next
Set pagenum = .FindElementByCss("a.next")
pagenum.SendKeys ("Keys.END")
Application.Wait Now + TimeValue("00:00:03")
pagenum.Click
If Err.Number <> 0 Then Exit Do
On Error GoTo 0
Loop
.Quit
End With
End Sub
Reference to add to the library:
Selenium Type Library