I have some VBA to launch a company intranet site which will bring me directly to the document I am searching for. I need to wait for the page to finish loading, and then hit the "Print" button which will open the document in a Adobe Reader supported IE tab, and from there I save it as a PDF to a drive.
My issue is that the loop I have to wait until the webpage is loaded does not properly wait. Doing some research on SO, I've seen this is a known issue with newer versions of IE. I have since tried playing with some XMLHTTP methods, but I am unfamiliar with those, and my attempts with it have also fallen short (not sure how I would navigate to the next page by hitting the Print link using XMLHTTP).
My current VBA is as follows, and ieApp is New InternetExplorerMedium.
Set objShell = CreateObject("Shell.Application")
IE_Count = 0
IE_Count = objShell.Windows.Count
For x = 0 To (IE_Count - 1)
On Error Resume Next
my_url = ""
my_title = ""
my_url = objShell.Windows(x).Document.Location
my_title = objShell.Windows(x).Document.Title
If my_url Like "http://ctdayppv002/Home/DocViewer?" & "*" Then
Set ie = objShell.Windows(x)
Do While ieApp.ReadyState <> 4 And ie.Busy
DoEvents
Loop
For Each ee In ie.Document.getElementsByTagName("a")
If ee.ID = "printDocLink" Then
ee.Click: DoEvents: Sleep 1500
Do While ie.ReadyState <> 4 And ie.Busy
DoEvents
Loop
Exit For
End If
Next ee
Exit For
Else
End If
Next
If I add a bunch of Sleep time, then it will wait, until a document comes up that exceeds the time I told it to Sleep, so obviously that isn't a reliable solution.
Using the following questions for reference, I have tried to use XMLHTTP, but also noticed comments that this method may not work with JavaScript sites.
VBA hanging on ie.busy and readystate check
web scraping with vba using XMLHTTP
One of my attempts with XMLHTTP:
Public ieApp As MSXML2.XMLHTTP60
Set ieApp = New MSXML2.XMLHTTP60
With ieApp
.Open "GET", urlString, False
.send
While ieApp.ReadyState <> 4
DoEvents
Wend
Dim HTMLDoc As MSHTML.HTMLDocument
Dim HTMLBody As MSHTML.HTMLBody
Set HTMLDoc = New MSHTML.HTMLDocument
Set HTMLBody = HTMLDoc.body
HTMLBody.innerHTML = ieApp.responseText
Debug.Print HTMLBody.innerHTML
End With
Within the resulting HTMLBody.innerHTML I do not see the "printDocLink" element.
FYI - I have been emailing a rep from the company that created the website database, and they do not believe there is an API call that can directly export as a PDF, which I was hoping would be available to skip over the "Print" button entirely.
Following the advice from Tim Williams and QHarr, I found a solution that works for me.
I added a Do Until, and also a timer for 6 seconds:
t = Now + TimeValue("0:00:6")
Do Until .Document.getElementById("printDocLink") <> 0
DoEvents: Sleep 1000
If Now > t Then
Call Not_Found_PPV(N, searchitem)
.Quit
Set ieApp = Nothing
GoTo NxtInv
End If
Loop
Related
Having some issues with my code, I've removed the URL since it requires specific access. I have confirmed the ID is correct and copy/pasted rather than manual typing. My code does open up IE, opens the correct URL, but does not paste "test" into the description box. What have I done wrong? I've bolded the line that returns the error.
Sub Test1()
Dim IE As Object
Dim doc As HTMLDocument
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
Url = "website"
With IE
.navigate Url
.Visible = True
'Waiting till page loads
Do While .readyState <> READYSTATE_COMPLETE
DoEvents
Debug.Print "Waiting on IE" & Time
Loop
End With
Application.Wait (Now + TimeValue("00:00:08"))
**IE.document.getElementById("new_call.description").Value = "test"**
End Sub
I have the following code using Internet Explorer which allows the page to load until the word "Fair Value" appears in the final HTML (which I then want to extract):
Dim t As Date,
Dim fTextFound as Boolean
Const MAX_WAIT_SEC As Long = 3 '<==Adjust wait time
myUrl = "https://finbox.com/NASDAQGS:AMZN/models/dcf-growth-exit-10yr"
Set oIE = New InternetExplorer
oIE.navigate myUrl
oIE.Visible = True
Do
Loop Until oIE.readyState = READYSTATE_COMPLETE
oIE.Refresh
While oIE.Busy Or oIE.readyState < 4: DoEvents: Wend
t = Timer
Do
DoEvents
On Error Resume Next
HTMLDoc.Body.innerHTML = oIE.document.Body.innerHTML
sPageHTML = HTMLDoc.Body.innerHTML
If InStr(LCase(sPageHTML), "fair value") > 0 Then ftextFound = True
If Timer - t > MAX_WAIT_SEC Then Exit Do
On Error GoTo 0
Loop While fTextFound= False
If fTextFound= True Then ......Do the rest
Since I believe that Internet Explorer will no longer be supported in Windows 11, can someone provide code that will mimic this functionality using "MSXML2.XMLHTTP.6.0"or similar in VBA?
I do have Selenium installed, so similar code for that would also be acceptable.
The idea is that it is a dynamic page which takes a few seconds to completely load, so just getting the .responseText is not sufficient.
Thanks.
I am trying to enter info into an Internet Explorer 11 website form. The website/form was designed a long time ago (around 15-20 years). The website can only be accessed through Internet Explorer.
I cannot share the website/source code as it is internal to my company.
I have browsed online for a solution, but none worked (I tried many different versions).
I am looking to login, then go through a few pages of entering information, while clicking next/submit at each stage. I am failing after I login.
I have the following references on Excel:
Microsoft Internet Controls, Microsoft HTML Object Library, Microsoft XML, v6.0
I am following the wise owl tutorial https://www.youtube.com/watch?v=dShR33CdlY8. Skip to about 17 mins in to see where I got the code.
I got an error message at the line htmlinput.Value = "excel".
The error message was
object variable or with block variable not set - Run Time error '91'
Sub navigate_website()
Dim ie As New SHDocVw.InternetExplorer
Dim htmldoc As MSHTML.HTMLDocument
Dim htmlinput As MSHTML.IHTMLElement
ie.Visible = True
ie.navigate Sheet1.Range("C2").Text
Do While ie.Busy Or ie.readyState <> READYSTATE_COMPLETE
DoEvents
Loop
'enter in userid
ie.document.forms("formsamplename").elements("usedid").Value = ThisWorkbook.Sheets("sheet1").Range("B6")
'enter in password
ie.document.forms("formsamplename").elements("userpassword").Value = ThisWorkbook.Sheets("sheet1").Range("B7")
'click the login button
ie.document.forms("formsamplename").elements("cmdSubmit").Click
Do While ie.Busy Or ie.readyState <> READYSTATE_COMPLETE
DoEvents
Loop
' ----- I tried the below code as an alternative but it didn't work -----
'ie.document.forms("formsamplename").elements("usernumber").Value = ThisWorkbook.Sheets("sheet1").Range("B6")
Set htmldoc = ie.document
Set htmlinput = htmldoc.getElementById("usernumber")
htmlinput.Value = "excel" **'error occurs here**
' ----- I also tried the below code, but it didn't work -----
'htmldoc.forms("formsamplename").elements("usernumber").Value = "test"
Set ie = Nothing
Set htmldoc = Nothing
Set htmlinput = Nothing
End Sub
I have written some code to scrape specific dates from Google's patent website. After reviewing lots of examples I figured out the getElementsByClassName that gets the date I need. The code below works when I step through in debug mode and generates the desired MsgBox. But when I run it, it gives me "Run-time error '91': Object variable or With block variable not set."
I have added delays wherever I thought that might be an issue. I have also disassociated the code from any interaction with the Excel spreadsheet where I would ultimately put the date, just to make it as simple as possible. I've also copied the code from the original spreadsheet to a new blank one, but same issue.
Any help would be appreciated.
Sub Get_Date()
Dim ie As InternetExplorer
Dim sURL As String
Dim strGrant As Variant
Set ie = New InternetExplorer
sURL = "https://patents.google.com/patent/US6816842B1/en?oq=6816842"
ie.navigate sURL
ie.Visible = False
Do While ie.Busy Or ie.ReadyState < 4
DoEvents
Loop
strGrant = ie.document.getElementsByClassName("granted style-scope application-timeline")(0).innerText
Do While ie.Busy Or ie.ReadyState < 4
DoEvents
Loop
MsgBox strGrant
ie.Quit
End Sub
````
It's likely a timing issue as per my comment. That's dealt with in other answers to similar questions. Main things to consider are:
Use proper page load waits: While IE.Busy Or ie.readyState < 4: DoEvents: Wend
Possibly a timed loop to attempt to set the element to a variable then testing if set.
Alternatively, a bit of a punt but it seems that all granted dates are the same as publication dates (patent publication date). If this is true then you can use xhr to get the publication date
Option Explicit
Public Sub GetDates()
Dim html As HTMLDocument, i As Long, patents()
patents = Array("US7724240", "US6876312", "US8259073", "US7523862", "US6816842B1")
Set html = New HTMLDocument
With CreateObject("MSXML2.XMLHTTP")
For i = LBound(patents) To UBound(patents)
.Open "GET", "https://patents.google.com/patent/" & patents(i) & "/en?oq=" & patents(i), False
.setRequestHeader "User-Agent", "Mozilla/5.0"
.send
html.body.innerHTML = .responseText
If html.querySelectorAll("[itemprop=publicationDate]").length > 0 Then
Debug.Print html.querySelector("[itemprop=publicationDate]").DateTime
End If
Next
End With
End Sub
I am trying to download a table of proprietary investments/positions/pricing from Nationwide. The code seems to do what I want, EXCEPT for producing an "object required" error when I attempt to select a particular account (click)
I thought I had the proper code to tell my macro to wait until IE was ready to go on, but clearly I am missing something.
In the code, the relevant line is highlighted. If I enter a STOP above the error line, I can wait until I "see" the link appear, then "continue" the code and it runs as expected.
Because this goes to my financial accounts, I cannot provide the user name and password to allow someone to replicate the exact problem, but here is the code, and the error message and highlight. Suggestions appreciated.
Option Explicit
'set Reference to Microsoft Internet Controls
Sub DownLoadFunds()
Dim IE As InternetExplorer
Dim sHTML
Const sURL As String = "https://www.nationwide.com/access/web/login.htm"
Const sURL2 As String = "https://isc.nwservicecenter.com/iApp/isc/app/ia/balanceDetail.do?basho.menuNodeId=12245"
Dim wsTemp As Worksheet
Set wsTemp = Worksheets("Scratch")
Set IE = New InternetExplorer
With IE
.Navigate sURL
.Visible = True 'for debugging
Do While .ReadyState <> READYSTATE_COMPLETE
DoEvents
Loop
Do While .Busy = True
DoEvents
Loop
'Login: User Name and Password "remembered" by IE
.Document.all("submitButton").Click
Do While .ReadyState <> READYSTATE_COMPLETE
DoEvents
Loop
Do While .Busy = True
DoEvents
Loop
'Select this account to show
.Document.all("RothIRA_#########").Click '<--Error at this line
Do While .ReadyState <> READYSTATE_COMPLETE
DoEvents
Loop
Do While .Busy = True
DoEvents
Loop
.Navigate sURL2
Do While .ReadyState <> READYSTATE_COMPLETE
DoEvents
Loop
Do While .Busy = True
DoEvents
Loop
Set sHTML = .Document.GetElementByID("fundByFundOnly")
With wsTemp
.Cells.Clear
.Range("a2") = sHTML.innertext
End With
.Quit
End With
Set IE = Nothing
End Sub
This is the error message:
This shows the highlighted line:
EDIT:
At Tim Williams suggestion, I added a loop to test for the presence of the desired element. This seems to work:
...
On Error Resume Next
Do
Err.Clear
DoEvents
Application.Wait (Time + TimeSerial(0, 0, 1))
.Document.getelementbyid("RothIRA_#########").Click
Loop Until Err.Number = 0
On Error GoTo 0
....
IE.Document.all("#RothIRA_....") is returning Nothing (null in more refined languages), so calling the Click method is causing the error.
Your code is the same as doing this:
Dim rothElement As Whatever
rothElement = IE.Document.all("#RothIRA_....")
rothElement.Click
...when you should do this:
Dim rothElement As Whatever
rothElement = IE.Document.all("#RothIRA_....")
If rothElement <> Nothing Then
rothElement.Click
End If
I suggest using the modern document.GetElementById method instead of the deprecated (if not obsolete) document.All API.
It's possible/likely that the page is using script to dynamically load some content or generate some layout after your "wait" loop has finished. That loop only waits until all linked content/resources have been loaded - it does not wait for scripts on the loaded page to finish, etc.
One approach is to loop your code waiting for the desired element to be rendered:
Const MAX_WAIT_SEC as Long = 5 'limit on how long to wait...
Dim t
t = Timer
Do While .Document.all("RothIRA_#########") Is Nothing
DoEvents
'or you can Sleep here
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop
'carry on...