I freely admit that I am not too experienced with the HTML Object Library.
I have a spreadsheet with IRS Employer Identification Numbers that I must identify as being in my database or not. I have only web-based access to this database to which some other people have written the HTML and manage the database. I believe their methods are antiquated and their design practices poor; but I'm not ultimately a database manager, so what do I know? As such, the normal practice on my end is to enter the EIN on the search page and note the result.
My Excel macro is meant to
Log in to the web-based database query site.
Loop through the EINs, noting which EINs are found
However, I have the following problems:
My code is as follows (though I've had to change the URLs):
Option Explicit
Sub FillFromWorkbookTest()
On Error GoTo ErrHandler
Const cURL = "https://www.someURL.com/LoginPage.jsp"
Const cUsername = "myUSERNAME"
Const cPassword = "myPASSWORD"
Dim IE As Object
Dim Doc As HTMLDocument
Dim LoginForm As HTMLFormElement
Dim UsernameInput As HTMLInputElement
Dim PasswordInput As HTMLInputElement
Dim LoginButton As HTMLInputButtonElement
Dim SearchForm As HTMLFormElement
Dim EINInput As HTMLInputElement
Dim SearchButton As HTMLInputButtonElement
Dim cEIN As String
Dim BotRow As Long
Dim EINRange As Range
Dim c As Variant
Dim i As Integer
Dim EINCheck As String
Dim EINCount As Integer
'## Open Browser & go to Admin Module, and Login
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.Navigate cURL
'## Wait for Adimn Module to load
Do Until IE.ReadyState = 4
DoEvents
Loop
'## Get the HTML Document of Admin Module login page (cURL)
Set Doc = IE.document
'## Get Admin Module login form
Set LoginForm = Doc.forms("f1")
'## Get Username input field and populate it
'## HTML: <input id=EIN type=text tabindex=3 size=9 maxlength=9 name=EIN title="Admin Code">
Set UsernameInput = LoginForm.elements("EIN")
UsernameInput.Value = cUsername
'## Get Password input field and populate it
'## HTML: <input id=PIN type=password tabindex=4 size=8 maxlength=8 name=PIN title="PIN">
Set PasswordInput = LoginForm.elements("PIN")
PasswordInput.Value = cPassword
'## Submit LoginForm
'## HTML: <input type=submit value=Login tabindex=5 title="Login"> (no onClick attribute; no element)
LoginForm.submit
Do Until IE.ReadyState = 4
DoEvents
Loop
'## Get the HTML Document of the new page
Set Doc = IE.document
'## Determine if login succeeded
If InStr(Doc.body.innerText, "Invalid Login.") = 0 Then
MsgBox "Login successful."
Else
MsgBox "Login failed."
End If
Debug.Print "Current URL: " & IE.LocationURL
'## Navigate to Global Change and reset HTML Document
IE.Navigate "https://www.someURL.com/LOGGED_IN/SomePage.jsp"
Do Until IE.ReadyState = 4
DoEvents
Loop
Set Doc = IE.document
'## Find last row in spreadsheet
BotRow = Worksheets("Sheet1").Range("A1").End(xlDown).Row
Set EINRange = Range("A1:A" & BotRow)
'## Set loop counter variable
i = 0
'## Cycle through IRS-identified EINs
For Each c In EINRange.Cells
cEIN = c.Value
i = i + 1
'## Get Admin Module login form
Set SearchForm = Doc.forms(0)
'## Get EIN input field and populate it
'## HTML: <input type="text" id=EIN name=EIN title="Enter charity EIN" maxlength=9 size=9 tabindex=11 >
Set EINInput = SearchForm.elements("EIN")
EINInput.Value = cEIN
'## Submit SearchForm
'## HTML: <input type="submit" value="Search" tabindex=15 title="Click here to search charity application" class="black_bold"
'## onclick="if (btn_OnClick(EIN,CODE)) {document.f1.action='SomeOther.jsp'; document.f1.submit(); return true;} else return false;" >
'## (has onClick attribute)
Set SearchButton = Doc.body.getElementsByTagName("table")(2). _
getElementsByTagName("tr")(0). _
getElementsByTagName("td")(0). _
getElementsByTagName("input")(2)
SearchButton.Click
Do Until IE.ReadyState = 4
DoEvents
Loop
'## Get the HTML Document of the new page
Set Doc = IE.document
'## Find EIN string on resulting page; Some number if found; Null if not
EINCheck = Doc.body.getElementsByTagName("table")(3).innerText
EINCount = InStr(1, EINCheck, cEIN, 1)
MsgBox EINCount
'## Determine which EINs are CFC charities
If InStr(1, EINCheck, cEIN, 1) = 0 Then
Worksheets("Sheet1").Range("F" & i).Value = "NO"
Else
Worksheets("Sheet1").Range("F" & i).Value = "YES"
End If
Next c
ErrHandler:
'## Cleanup
MsgBox "Error" & Err.Number & ": " & Err.Description
Set IE = Nothing
Set Doc = Nothing
Set LoginForm = Nothing
Set UsernameInput = Nothing
Set PasswordInput = Nothing
Set LoginButton = Nothing
Set SearchForm = Nothing
Set EINInput = Nothing
Set SearchButton = Nothing
End Sub
Any suggestions?
I've found better success using the following for the "wait until IE is ready"
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Function IEWait(p_ieExp As InternetExplorer)
'this should go from ready-busy-ready
Dim initialReadyState As Integer
initialReadyState = p_ieExp.ReadyState
'wait 250 ms until it's done
Do While p_ieExp.Busy Or p_ieExp.ReadyState <> READYSTATE_COMPLETE
Sleep 250
Loop
End Function
You'd call it like
IEWait IE 'your internet explorer is named "IE"
I was running into far too many quirky errors with using only one of the conditions for "ready." After modifying my "ready" checks to that method, this has nearly 100% gone away. Sometimes the ready state does not reflect the state accurately.
Regarding your first questions, using the Sleep
method I reference above, try adding Sleep 1000
or so before each of your commands to verify the problem is in your logic, and not in IE loading too slowly. Or stepping through with a debugger slowly.
What you are describing sounds very similar to some issues I've had when IE would partially load and my code would then continue to execute.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With