VBA Object Required Error Trying To Get InnerText - vba

I am trying to create a code that will go to a website, put in data, submit it, and then return the answer to a cell in excel. When I step through it, it works fine, but when I just try to run it, I get run-time error 424; Object Required.
I tried looking for a good answer, but I am just not grasping on how to quite fix this. Where is my issue? How do I correct it?
Sub Distance()
Dim IE As Object
' Create InternetExplorer Object
Set IE = CreateObject("InternetExplorer.Application")
' Make visible
IE.Visible = True
' Go to site
IE.Navigate "http://www.distance-cities.com/"
' Wait while IE loading...
Do Until IE.READYSTATE = 4
DoEvents
Loop
IE.Document.getelementbyId("from").Value = "Stillwater, OK"
IE.Document.getelementbyId("to").Value = "Hollis, OK"
IE.Document.forms(0).submit
Do Until IE.READYSTATE = 4
DoEvents
Loop
'*Below is where I get my error
Sheet1.Range("E5").Value = IE.Document.getelementbyId("routemi").InnerText
IE.Quit
End Sub
I apologize if this is a bit messy.
Thanks for you help!

Something like this (untested):
Dim el as object
'...
Set el = WaitForElement(IE.Document, "routemi", 1)
If Not el is Nothing Then
Sheet1.Range("E5").Value = el.innerText
Else
Msgbox "Element 'routemi' not found!"
Exit Sub
End if
Utility function to get an element by id, waiting until it appears:
Function WaitForElement(doc As Object, id As String, maxWaitSec As Double) As Object
Dim rv As Object, t
t = Timer
Do
Set rv = doc.getElementById(id)
DoEvents
Loop While rv Is Nothing And (Timer - t) < maxWaitSec
Set WaitForElement = rv
End Function

You need to leave proper waits at each point (after .Navigate2 and .Click) to allow for page loading. I use CSS selectors to target ids as modern browsers are optimized for CSS. Also, I updated method for page navigation to .Navigate2.
Option Explicit
Public Sub Distance()
Dim ie As New InternetExplorer
With ie
.Visible = True
.Navigate2 "http://www.distance-cities.com/"
While .Busy Or .readyState < 4: DoEvents: Wend
With .document
.querySelector("#from").Value = "Stillwater, OK"
.querySelector("#to").Value = "Hollis, OK"
.querySelector("[type=submit]").Click
End With
While .Busy Or .readyState < 4: DoEvents: Wend
Debug.Print .document.querySelector("#routemi").innerText
.Quit
End With
End Sub

Related

Select option from dropdown menu with VBA

I am trying to scrape data from this website: https://portal.emsa.europa.eu/widget/web/thetis/inspections/-/publicSiteInspection_WAR_portletpublic
I need to input three values, the "Period" dates (which is ok) and select the "Flag" (in this case Portugal). This last one has proven to be a huge difficulty since typing is not an option.
Private Sub Run()
Dim objIE As Object
Set objIE = CreateObject("InternetExplorer.Application")
objIE.Visible = True
objIE.navigate ("https://portal.emsa.europa.eu/widget/web/thetis/inspections/-/publicSiteInspection_WAR_portletpublic")
Application.Wait (Now + TimeValue("0:00:13"))
objIE.document.getElementById("tdate-1028-inputEl").Value = "01/01/2019"
objIE.document.getElementById("tdate-1029-inputEl").Value = "01/09/2019"
objIE.document.getElementById("checkcombo-1014-trigger-picker").Click
If objIE.document.getElementByClass("x-boundlist-item") = """Portugal""" Then objIE.document.getElementBy("x-combo-checker").Click
End Sub
You want a timed loop waiting for presence of element to click on as well as to click parent first. Sadly, I can't see an easy way to knock out the current explicit wait time used at beginning for page load to complete
Option Explicit
Public Sub SelectFlag()
Dim ie As SHDocVw.InternetExplorer, t As Date
Const MAX_WAIT_SEC As Long = 5 '<==adjust time here
Set ie = New SHDocVw.InternetExplorer
With ie
.Visible = True
.Navigate2 "https://portal.emsa.europa.eu/widget/web/thetis/inspections/-/publicSiteInspection_WAR_portletpublic"
While .Busy Or .readyState <> 4: DoEvents: Wend
With .document
Application.Wait Now + TimeSerial(0, 0, 5)
.querySelector("#checkcombo-1014-trigger-picker").Click
Dim ele As Object
t = Timer
Do
On Error Resume Next
Set ele = .querySelector("[data-recordid='246']")
On Error GoTo 0
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While ele Is Nothing
If Not ele Is Nothing Then ele.Click
End With
Stop '<==Delete me later
'.quit
End With
End Sub
I have meanwhile tried another option but still without success:
Private Sub Run()
Dim objIE As Object
Set objIE = CreateObject("InternetExplorer.Application")
objIE.Visible = True
objIE.navigate ("https://portal.emsa.europa.eu/widget/web/thetis/inspections/-/publicSiteInspection_WAR_portletpublic")
Application.Wait (Now + TimeValue("0:00:13"))
Dim iL As IHTMLElement
Dim e As IHTMLElementCollection
Set e = appIE.document.getElementById("checkcombo-1014-picker").getElementsByClass("x-boundlist-item")
For Each iL In e
If iL.innerText = "Portugal" Then
iL.Click
End If
Next iL
End Sub

How to exit loop when scrollBy reaches the bottom of the web page?

I've written a script in VBA using IE to reach the bottom of a web page automatically. The web page displays it's content in such a way that if I scroll downward more products become visible. I have used .scrollBy within my script to handle the lazy load.
I don't understand how to stop the scrolling when there is no more new products to load - I've used .scrollBy within a Do loop. How can I exit my loop when the scrolling is done and the browser reaches the bottom of the web page? Thanks in advance for any solution.
This is what I've tried so far:
Sub HandleLazyload()
Const URL As String = "https://www.inc.com/profile/sumup-payments-limited"
Dim IE As New InternetExplorer, HTML As HTMLDocument, post As Object
With IE
.Visible = True
.navigate URL
While .Busy = True Or .readyState < 4: DoEvents: Wend
Set HTML = .document
End With
Do
HTML.parentWindow.scrollBy 0, 99999
Application.Wait Now + TimeValue("00:00:03")
Set post = HTML.getElementsByTagName("article")
Loop ''I wish to break out of this loop when all the scrolling is done
IE.Quit
End Sub
Try the following where I used the rank to determine termination/exit of loop.
Option Explicit
Public Sub HandleLazyload()
Const URL As String = "https://www.inc.com/profile/sumup-payments-limited"
Dim IE As New InternetExplorer, HTML As HTMLDocument
With IE
.Visible = True
.navigate URL
While .Busy = True Or .readyState < 4: DoEvents: Wend
Set HTML = .document
End With
Dim rank As Long, item As Long
item = 1
Do While Err.Number = 0
HTML.parentWindow.scrollBy 0, 99999
Application.Wait Now + TimeSerial(0, 0, 1)
On Error GoTo errhand
rank = Split(HTML.querySelectorAll(".rank dt ~ dd")(item).innerText, "#")(1)
item = item + 1
Loop
errhand:
Err.Clear
Debug.Print "Stopped at rank " & rank
'Your other code
'IE.Quit
End Sub
Notes:
CSS Selector:
In case you want to know more about the CSS selector
The selector below targets all elements where class name is rank and then has sibling elements dt and dd within.
HTML.querySelectorAll(".rank dt ~ dd")(item)
Targeted HTML:

Capturing the IE windows using For Each window shell application

I need to capture the windows and need to navigate further, I am able to capture using the below code,
With objIE
Application.Wait (Now + TimeValue("00:00:05"))
Set TBL = .document.getElementById("payersitecredential-table")
Set PRV = TBL.getElementsByTagName("A"): PRV.Item(1).Click
'once the above code runs, it opens up an another window and i need to set that window as object.
Application.Wait (Now + TimeValue("00:00:20"))
End With
For Each WINDW In CreateObject("Shell.Application").Windows
If WINDW.Name = "Internet Explorer" Then
If InStr(WINDW.LocationURL, "newmmis") <> 0 Then
Set objIE1 = WINDW
Exit For
ElseIf InStr(WINDW.LocationURL, "https://www.example.com") <> 0 Then
Set ClsWindw = WINDW
ClsWindw.Quit
GoTo ThEnD
End If
End If
Next
With objIE1
Do While .Busy Or .readyState <> 4 'this is the place i get error as object required
DoEvents
Loop
Application.Wait (Now + TimeValue("00:00:05"))
.document.getElementById("InquireStatus").Click
End With
i have kept a wait time of 60 seconds for the URL to load, but sometimes the URL takes much time to load, at that time it is unable to set the object, could you guys let me figure out this and give me a solution.
Please see this in support of my comment
Sub ex()
Dim ie As SHDocVw.InternetExplorer
Dim wins As New SHDocVw.ShellWindows
For Each ie In wins
Debug.Print ie.Document.URL, ie.ReadyState, ie.Busy
Next ie
End Sub
EDIT
I would utilise the above, like so, this will find the IE based on URL and wait until it's ready and return, so use set IE=GetMeReadyIE("http://stackoverflow.com/questions/tagged/vba") for example.
Function GetMeReadyIE(strURL As String) As SHDocVw.InternetExplorer
Dim ie As SHDocVw.InternetExplorer
Dim wins As New SHDocVw.ShellWindows
For Each ie In wins
If ie.Document.URL = strURL Then
While ie.ReadyState <> READYSTATE_COMPLETE Or ie.Busy
DoEvents
Wend
Set GetMeReadyIE = ie
Exit For
End If
Next ie
End Function

Error received when downloading a file from web when only submit button is present

I need to download a file from web, but only a submit button is present.
There is no url for reaching the file directly.
I tried to catch the button by its id, but VBA drops run-time error '424' (Object required).
Here is the code:
Sub keler_submit()
Application.ScreenUpdating = False
Set IE = CreateObject("InternetExplorer.Application")
IE.navigate "https://www.keler.hu/T%C3%A1rsas%C3%A1gi%20esem%C3%A9nyek/"
While IE.Busy
DoEvents
Wend
Set doc = IE.document
gomb = doc.getElementById("ctl00_wpm_UserControlPortlet1682286671_ctl00_wpm_UserControlPortlet1682286671_userControlPortlet_DownloadButton1")
gomb.submit
Set IE = Nothing
Application.ScreenUpdating = True
End Sub
Thank you in advance
These lines are incorrect:
gomb = doc.getElementById("ctl00_wpm_UserControlPortlet1682286671_ctl00_wpm_UserControlPortlet1682286671_userControlPortlet_DownloadButton1")
gomb.submit
doc.getElementById returns an object type so you need to use Set to assign it to a variable. The element you are trying to access should then be clicked instead of submitted. There's a high probability that IE will display a "Do you wish to open or save this file" dialog so start by making the IE object visible to show this:
IE.visible = True
Set gomb = doc.getElementById("ctl00_wpm_UserControlPortlet1682286671_ctl00_wpm_UserControlPortlet1682286671_userControlPortlet_DownloadButton1")
gomb.click
You need a loop in there as well to ensure page has loaded. The following works:
Option Explicit
Public Sub GetInfo()
Dim IE As New InternetExplorer
With IE
.Visible = True
.navigate "https://www.keler.hu/T%C3%A1rsas%C3%A1gi%20esem%C3%A9nyek/"
While .Busy Or .readyState < 4: DoEvents: Wend
Do
Dim b As Object
On Error Resume Next
Set b = .document.getElementById("ctl00_wpm_UserControlPortlet1682286671_ctl00_wpm_UserControlPortlet1682286671_userControlPortlet_DownloadButton1")
On Error GoTo 0
Loop While b Is Nothing
b.Click
'Handle save button
'Quit '<== Remember to quit application
End With
End Sub

VBA IE Automation

I am trying to automate a process where I must query a website: http://rgl.faa.gov/Regulatory_and_Guidance_Library/rgAD.nsf/MainFrame?OpenFrameset
There is a input text field <input name="query size="20"/>" that I want to populate but I am struggling to do so. Currently I am testing my code to see if I can even reference the tag.
Sub fill()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.navigate "http://rgl.faa.gov/Regulatory_and_Guidance_Library/rgAD.nsf/MainFrame?OpenFrameset"
IE.Visible = True
While IE.busy
DoEvents
Wend
For Each it In IE.Document.getElementsByTagName("input")
If it.Name = "newquery" Then
MsgBox ("yup")
End If
Next
End Sub
I think my issue is that the input field is in 2 framesets and a frame...
Any ideas if this is even possible to do?
Unless you know exactly which frame the input is in, this is a classical situation for a recursive search:
Sub fill()
Dim IE As Object, elem As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.navigate "http://rgl.faa.gov/Regulatory_and_Guidance_Library/rgAD.nsf/MainFrame?OpenFrameset"
IE.Visible = True
While IE.Busy: DoEvents: Wend
While IE.document.readyState <> "complete": DoEvents: Wend
Set elem = FindInputByName(IE.document, "newquery")
If Not elem Is Nothing Then
elem.Value = "It works!"
End If
End Sub
Function FindInputByName(document As Object, name As String) As Object
Dim i As Integer, subdocument As Object, elem As Variant
Set FindInputByName = Nothing
For i = 0 To document.frames.Length - 1
Set subdocument = document.frames.Item(i).document
Set FindInputByName = FindInputByName(subdocument, name)
If Not FindInputByName Is Nothing Then Exit Function
Next i
For Each elem In document.getElementsByTagName("INPUT")
If elem.name = name Then
Set FindInputByName = elem
Exit Function
End If
Next elem
End Function
You should try to install and use opentwebst Library : http://codecentrix.com/download.html
Don't forget to add opentwebst in project references.
It took me less time to generate the code below then writing this post :
Sub OpenTwebstMacro
Dim core As ICore
Set core = New OpenTwebstLib.core
Dim browser As IBrowser
Set browser = core.StartBrowser("http://rgl.faa.gov/Regulatory_and_Guidance_Library/rgAD.nsf/MainFrame?OpenFrameset")
Call browser.FindElement("input text", "name=newquery").InputText("YOUR QUERY")
Call browser.FindElement("input button", "uiname=Go").Click
End Sub