VBA:Selenium: run time error on Foreach statement? - vba

I'm getting an error "Object doesn't support this property or method"
Below is my coding,
Dim webs, web As Object
Set webs = driverg.findElementByCssSelector("*[class^='title-link id-track-click']")
**For Each web In webs**
Debug.Print web.Text
Next web
When program starts in foreach, I'm getting this error.
Is there any another method looping Web-element as I have tried a lot, but I can't solve this?

You've defined webs as a Variant
By putting them on same line it doesn't default to the declaration at the end
Define them on their own separate lines
Dim webs As Object
Dim web As Object
Or if that's not what you intended and web is an array then use
Dim webs As Object
Dim web As Variant
EDIT - check docs
According to docs - it only returns the first element - so I'm not sure why you're doing a "For each"
Type: IWebElement The first IWebElement matching the criteria.

I have changed findElementByCssSelector into findElementsByCssSelector, as findElementByCssSelector return only one element hence that for each not wroking but when changed into findElemenstByCssSelector, getting working.
I have changed below line
driverg.findElementByCssSelector("*[class^='title-link id-track-click']")
to
driverg.findElementsByCssSelector("*[class^='title-link id-track-click']")

Related

Late Binding & Type Issues In VB

I'm trying to run my code which was originally created using Visual Studio through another application where late bindings are disallowed and this option cannot be altered unfortunately. I am very new to programming in general and struggling to get my head around the issue. Here is the code im using in the invoke code stage:
Dim objIEShell As Object = CreateObject("Shell.Application")
Dim objIEShellWindows As Object = objIEShell.Windows
Dim objIEWin As Object
For Each objIEWin In objIEShellWindows
If InStr(objIEWin.LocationURL,"google")>0 Then
objIEWin.Quit
objIEWin = Nothing
End If
Next
The code simply closes all instances of Internet Explorer with "google" in the URL. This is the error message I get when trying to compile it:
Message: Error compiling code
error BC30574: Option Strict On disallows late binding. At line 2
error BC32023: Expression is of type 'Object', which is not a collection type. At line 4
From the research I've done so far I realise the first error message on line 2 is to do with the type difference between objIEShell and the Windows method. I think I have to convert objIEShell like this, CType(objIEShell,?), but I don't know the type of the .Windows method or how to find this out. Also any insight on how the fix the second error would be greatly appreciated as I'm not sure where to start with that one either.
This dates back to the wonky days when Microsoft still had plans to make Explorer behave like a web browser. Makes it pretty hard to arrive at the correct code, it is a combination of two separate COM components that don't have much to do with each other.
You need to first add two references to those components so the compiler understands the names. Use Project > Add Reference > COM tab and tick "Microsoft Internet Controls" and "Microsoft Shell Controls and Automation". That adds the Shell32 and SHDocVw namespaces.
Now you can write the code early-bound like this:
Dim objIEShell = New Shell32.Shell
Dim objIEShellWindows = CType(objIEShell.Windows, SHDocVw.IShellWindows)
Dim objIEWin As SHDocVw.WebBrowser
For Each objIEWin In objIEShellWindows
If InStr(objIEWin.LocationURL, "google") > 0 Then
objIEWin.Quit()
End If
Next
The CType() expression is probably the most unintuitive one, the Shell.Windows property is of type Object to break the dependency between those two components. The cast is the necessary voodoo to keep the compiler happy.

How to get an object out of a collection in VBa

I used to have the following working code
Set result = DecodeJson(MyRequest.responseText)
Dim keys() As String
keys = GetKeys(result.issues)
Now, my approach has changed and instead of having result being the object, I receive a Collection based upon Array of class objects as class member in VBA
Dim resultsFromQueries As Collection
Set resultsFromQueries = GetAllJSonObjects(searchString) ' this calls DecodeJson(MyRequest.responseText) as per the code snippet above
Dim i As Integer
For i = 0 To resultsFromQueries.Count
Dim keys() As String
Dim item As Object
Set item = resultsFromQueries.item(i + 1) ' I guess it's not 0 based?
keys = GetKeys(item.result.issues) 'KABOOM
The issue I have now is I keep getting the following exception
Run time error '424':
Object required
Checking in the watch window, item shows as type Variant/String and has the value of "[object Object]"
Do I need to cast it?
I had that problem just a few weeks ago, the problem was the following:
The collection was collected with the follwing code:Collection.Add(OBJECT)
This however leads to the addition of the value of the Object into the Collection and can be solved as simple as leaving the parathesis.Collection.add OBJECT
So my advice is to go into the GetAllJSonObjects(searchString) function and
search for the addition process and make sure that it is without parethesis.
Like this the Collection will contain Objects and not variants and your code with item(i+1) should work properly.
Good luck!

Scrape html data Vba

I want to make a function that extracts data from a part of a site.
The following is the HTML site. HTML code.
Code for the function
Function GetElementById(url As String, id As String, Optional isVolatile As Boolean)
Application.Volatile (isVolatile)
On Error Resume Next
Dim html As Object, objResult As Object
ret = GetPageContent(url)
Set html = CreateObject("htmlfile")
html.Body.innerHtml = ret
Set objResult = html.GetElementById(id)
GetElementById = objResult.innerHtml
End Function
I need that extracts only the class "panel-body"
directly into the function. I think it would be .children (3). Is that correct?
And so that it is practical and fast, because I need to extract more than 50 sites.
I see at least two options.
Once you have the HTMLDivElement with id=Result you could simply get the children. Please test this by first doing objResult.Children(2) and checking what the element is that is returned.
objResult.Children(2).Children(0).Children(0)
The second is that in later versions of MSHTML I think with IE8 or later installed you have the method "GetElementsByClassName" This will return a collection of IHTMLElements. If the HTMLDocument only has 1 "panel-body" then you are in luck. If not you would need to loop through each one and check some other unique feature to know you have the right one.
Another way to generate the code for this job is to record a macro, then add a loop around the recorded macro that loops through your 50 pages and gets the results.
On the data tab in the ribbon there is an option get data from external sources. If you use this it's gives you a point and click interface that let's you chose the table your looking for. Record a macro while your doing this and it generates the code for you.

How to work with result collections from Selenium

Or how to work with collections (or arrayss) in VBA.
The issue is most probably myself, but I couldn't find an answer yet.
I am trying to go trough a some pages on a web-site with Selenium-vba to find some data.
As usual if there is more to display, the site shows a 'NEXT' button. The button has <a href ... > when the link is activated, else it's just plain text.
To test if there is another page I have found the way to use findElementsByLinkText, and either there is a link or the the collection is empty. So this can be tested by the size of the collection.
This works so far.
But when I try to use the collection (aside from a for each loop) for further action I can't get it to operate.
This is the code:
Dim driver As New SeleniumWrapper.WebDriver
Dim By As New By, Assert As New Assert, Verify As New Verify, Waiter As New Waiter
On Error GoTo ende1
driver.Start "chrome", "http://www.domain.tld/"
driver.setImplicitWait 5000
driver.get "//......."
Set mynext = driver.findElementsByLinkText("Next")
if mynext.Count >0 Then
mynext(1).Click 'THIS STATEMENT DOES NOT WORK
End If
So please help me to get around my understanding issue (which I am convinced it is)
How can I access an element from the collection.
My workaround so far is to execute
driver.findElementByLinkText("Next").Click
but this is unprofessional as it executes the query again.
The Next button is probably loaded asynchonously after the page is completed.
This implies that findElementsByLinkText("Next") returns no elements at the time it's called.
A way to handle this case is to silent the error, adjust the timeout and test the returned element:
Dim driver As New Selenium.ChromeDriver
driver.Get "https://www.google.co.uk/search?q=selenium"
Set ele = driver.FindElementByLinkText("Next", Raise:=False, timeout:=1000)
If Not ele Is Nothing Then
ele.Click
End If
driver.Quit
To get the latest version in date working with the above example:
https://github.com/florentbr/SeleniumBasic/releases/latest

COM Exception (code 0x800A03EC) thrown when programmatically changing page breaks in VB.Net

I am attempting to use the VB.Net Excel COM interop to programmatically change the location of the first horizontal page break on an Excel spreadsheet being generated by the program. Code to do so is as follows:
Dim range As Excel.Range
xlActualWS.Activate()
xlActualWS.PageSetup.PrintArea = "$A$1:$K$68"
range = xlActualWS.Range("A68", "A68")
xlActualWS.HPageBreaks(1).Location = range
System.Runtime.InteropServices.Marshal.ReleaseComObject(range)
On the line setting HPageBreaks, COM exception code 0x800A03EC is thrown, and I can't really find a thing related to this searching. Anyone have any idea what I'm missing here?
Based onthe code it looks like either the location of the page break cannot be set or that there are 0 page breaks and hence you're accessing an invalid index. A quick way to test this out is to do the following
Check the Count property on xlActualWS.HPageBreaks and see how many are available
Remove the set of the Location property and see if the error dissapears
Additionally you should probably remove the ReleaseComObject call. That's a very difficult API to get correct and the CLR is quite good at cleaning up COM object references on it's own.