VBA logging in to website with 2 step login - html

as title says i am having an error at htdoc.all.verificationcode.Value = otp
it says Run-time error '438':
Object doesn't support this property or method
i have spent an afternoon trying to find what is wrong with it and i really hope that you guys could help me out.
Dim HTMLDoc As HTMLDocument
Dim htdoc As HTMLDocument
Dim MyBrowser As InternetExplorer
Sub login()
Dim username As Range
Dim password As Range
Dim otp As Range
Dim myValue As Variant
Dim MyHTML_Element As IHTMLElement
Dim MyURL As String
MyURL = "XXXXXXXXXXXXXXXXXXXXXX"
Set MyBrowser = New InternetExplorer
MyBrowser.silent = True
MyBrowser.navigate MyURL
MyBrowser.Visible = True
Set username = Range("B1")
Set password = Range("B2")
Set otp = Range("B3")
Do
Loop Until MyBrowser.readyState = READYSTATE_COMPLETE
Set HTMLDoc = MyBrowser.document
HTMLDoc.all.UserId.Value = username
HTMLDoc.all.password.Value = password
For Each MyHTML_Element In HTMLDoc.getElementsByTagName("input")
If MyHTML_Element.Type = "submit" Then MyHTML_Element.Click: Exit For
Next
MyURL2 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
MyBrowser.silent = True
MyBrowser.navigate MyURL2
MyBrowser.Visible = True
myValue = InputBox("Enter OTP")
Range("B3").Value = myValue
Application.Wait (Now + TimeValue("0:00:10"))
Do
Loop Until MyBrowser.readyState = READYSTATE_COMPLETE
Set htdoc = MyBrowser.document
htdoc.all.verificationcode.Value = otp
For Each MyHTML_Element In htdoc.getElementsByTagName("input")
If MyHTML_Element.Type = "btnobj" Then MyHTML_Element.Click: Exit For
Next
Err_Clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
End Sub
HTML in the webpage for the form is
<input name="verficationcode" id="verficationcode" type="password" size="18" maxlength="16" autocomplete="off">
<input name="btnobj" class="inputbutton" id="submitButton" onclick="checkSubmit(this);" type="button" value="Submit">

OK. That looks like a good start. You are going to a page, inputting some values and clicking Submit>. You have not set MyBrowser.visible to False so I'm assuming that you can see this going on.
The first thing you need is to wait while MyBrowser loads the page after you .click submit>. You will need to wait every time you go to a new page; My favorite is to put all of the waiting into a single line like this.
MyHTML_Element.Click
Do While (MyBrowser.Busy Or MyBrowser.readyState <> READYSTATE_COMPLETE): DoEvents: Loop
When you have that new page, you need to locate your OTP and store it in a variable. Look at the HTML behind the new page. You might see something like this:
<div id='One_Time_Password'>
47665489_29751678
</div>
You can put this into a variable you have previously declared like this.
dim myOTP as string
myOTP = MyBrowser.getElementsById("One_Time_Password").innerText
Now you have the password stored and you can do something with it.

Related

How to deal with Hidden Elements on VBA Selenium [duplicate]

I am trying to automate a report through VBA. I have worked in VBA but not able to login in iTunes website through codes. Someone told me that it is written in IFrame, but i have no idea. Even i am not able to put my username in input box of login page.
https://itunesconnect.apple.com/login
Dim HTMLdoc As HTMLDocument
Dim MyBrowser As InternetExplorer
Sub check()
Dim MyHTML_element As IHTMLElement
Dim MyURL As String
MyURL = "https://itunesconnect.apple.com/login"
Set MyBrowser = New InternetExplorer
MyBrowser.Silent = True
MyBrowser.navigate MyURL
MyBrowser.Visible = True
Do
Loop Until MyBrowser.readyState = READYSTATE_COMPLETE
Set HTMLdoc = MyBrowser.document
HTMLdoc.getElementsByID("account_name_text_field").Value = "username#outlook.com"
HTMLdoc.all.Password.Value = "password"
For Each MyHTML_element In HTMLdoc.getElementsByTagName("input")
If MyHTML_element.Type = "sign-in" Then MyHTML_element.Click: Exit For
Next
Err_Clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
End Sub
API sounds like a good idea. The whole page is very slow loading (for me at least) and there is an iframe to navigate.
I would go with selenium basic wrapper for vba and switch to the iframe. I will try to improve this when I have time but for now this works.
After installing selenium you will need to add a reference via VBE > Tools > References to Selenium Type library.
Option Explicit
Public Sub EnterInfo()
Dim d As WebDriver, t As Date, ele As Object
Set d = New ChromeDriver
Const URL = "https://itunesconnect.apple.com/login"
Const WAIT_TIME_SECS As Long = 30
t = Timer
With d
.Start "Chrome"
.get URL
Do
DoEvents
If Timer - t > WAIT_TIME_SECS Then Exit Do
On Error Resume Next
.SwitchToFrame "aid-auth-widget-iFrame"
Set ele = .FindElementByCss("#account_name_text_field")
On Error GoTo 0
Loop While ele Is Nothing
If ele Is Nothing Then Exit Sub
ele.SendKeys "Joe.Bloggs#aol.com"
.FindElementByCss("#sign-in").Click
'Other code....
Stop '<=Delete me later
.Quit
End With
End Sub

Failing to Set Input Box Values on Website

I have the following VBA code:
Sub OpenWebPage()
Dim oIE As Object
Dim sURL As String
Dim HTML As HTMLDocument, hDataManager As IHTMLElementCollection, hDropSelect
Dim HWNDSrc As Long
Dim itm As Variant
'objects
Set oIE = CreateObject("InternetExplorer.Application")
'//---Open the browser and log in, then navigate to the data manager
oIE.silent = True 'No pop-ups
oIE.Visible = True
sURL = "https://publicisuk.lumina.mediaocean.com/Admin/DataManager.aspx"
oIE.navigate sURL
'wait for process to complete before executing the next task
Do While oIE.Busy: DoEvents: Loop
Do Until oIE.ReadyState = 4: DoEvents: Loop
'get window ID for IE so we can set it as active window
HWNDSrc = oIE.HWND
'set IE as active window
SetForegroundWindow HWNDSrc
'loop through elements/ items to find username field
For Each itm In oIE.document.All
If itm = "[object HTMLInputElement]" Then
If itm.Name = "username" Then
Debug.Print "Username field found!"
itm.Value = "johnsmith#gmail.com"
Debug.Print "Username: " & itm.Value
Exit For
End If
End If
Next itm
'now loop through to find password element
For Each itm In oIE.document.All
If itm = "[object HTMLInputElement]" Then
If itm.Name = "password" Then
Debug.Print "Password field found!"
itm.Value = "IAmAMonkey"
Debug.Print "Password: " & itm.Value
Application.SendKeys "~", True
Exit For
End If
End If
Next itm
What it does is loads Internet Explorer to an object, and then navigates to a URL where I want to log in. I loop through the elements on the web-page checking for InputElements. When the Username input box is found it sets the .Value to the username, and when the Password input box is found it sets the .Value to the password, after which I send the Enter key, presumably to log in.
Now, the interesting problem. When I run this it prints out that it finds the respective fields, and also prints out their newly set values, however, no text appears in the boxes at all.
I have also tried the following syntax:
oIE.Document.getElementById("username").Value = "johnsmith#gmail.com"
oIE.Document.getElementsByName("username").Value = "johnsmith#gmail.com"
With the same result - there is no error, but the values aren't showing up in the boxes.
Does anyone know why this would be the case or what is wrong with my approach?
Below is the HTML code.
<div id="login-panel">
<input name="username" class="form-required-field" id="username" accesskey="u" type="text" placeholder="Username" value="" data-bind="value: form().username, placeholder: bundle['user.label.html']" htmlescape="true" autocomplete="off" cssclass="form-required-field"><!-- organization select goes here if multiple organizations use the same domain --><div class="inline-organization-panel" id="inline-organization-panel" style="display: none;" data-bind="moSlideVisible: isOrganisationSelectPanelVisible()">
<span id="inline-organization-label" data-bind="text: bundle['organization.panel.label']">Choose organization.</span>
<div data-bind="foreach: organisationInfoArray"></div>
</div>
<input name="password" class="form-required-field" id="password" accesskey="p" type="password" placeholder="Password" value="" data-bind="value: form().password, placeholder: bundle['password.label.html']" htmlescape="true" autocomplete="off" cssclass="form-required-field" csserrorclass="error"><!-- Continent selection goes here if mf-style username and don't have continent cookie -->
I would focus on each element before assigning value then you simply need to remove the disabled attribute from the submit button
Option Explicit
Public Sub LogIn()
Dim ie As SHDocVw.InternetExplorer
Set ie = New SHDocVw.InternetExplorer
With ie
.Visible = True
.Navigate2 "https://publicisuk.lumina.mediaocean.com/mo-cas/login"
While .Busy Or .readyState <> 4: DoEvents: Wend
With .document
With .querySelector("#username")
.Focus
.Value = "johnsmith#gmail.com"
End With
With .querySelector("#password")
.Focus
.Value = "IAMaMonkey"
End With
With .querySelector("#buttonSignin")
.removeAttribute "disabled"
.Click
End With
End With
Stop
End With
End Sub
Here is where I got to, i'll try again a little later, but may help in the meantime.
Dim ie As InternetExplorer
Dim d As HTMLDocument
Dim f As HTMLFormElement
Set ie = New InternetExplorer
ie.Visible = 1
ie.navigate "https://publicisuk.lumina.mediaocean.com/Admin/DataManager.aspx"
Do While ie.Busy Or ie.readyState <> READYSTATE_COMPLETE
DoEvents
Loop
Set d = ie.document
Set f = d.forms(0)
'Putting a break point here and waiting a moment allows access
f.elements("username").Value = "username"
I tested and found that the code would run when setting breakpoints before setting value but wouldn't run without breakpoints.
I think it could be that debug mode adds more delay so I tried to add a separate delay in the code and it works. You could check my sample below:
Sub LOADIE()
Set ieA = CreateObject("InternetExplorer.Application")
ieA.Visible = True
ieA.navigate "https://publicisuk.lumina.mediaocean.com/mo-cas/login"
Do Until ieA.readyState = 4
DoEvents
Loop
delay 4
Set doc = ieA.Document
Set UserName = doc.getElementById("username")
UserName.Value = "johnsmith#gmail.com"
Set Password = doc.getElementById("password")
Password.Value = "IAmAMonkey"
Set btn = doc.getElementById("buttonSignin")
btn.Disabled = False
btn.Click
End Sub
Private Sub delay(seconds As Long)
Dim endTime As Date
endTime = DateAdd("s", seconds, Now())
Do While Now() < endTime
DoEvents
Loop
End Sub

Pull value from website (HTML div class) using Excel VBA

I'm trying to automate going to a website and pulling the ratings from several apps.
I've figured out how to navigate and login to the page.
How do I pull the element - the number "3.3" in this case - from this specific section into Excel.
Being unfamiliar with HTML in VBA, I got this far following tutorials/other questions.
Rating on website and the code behind it
Sub PullRating()
Dim HTMLDoc As HTMLDocument
Dim ie As InternetExplorer
Dim oHTML_Element As IHTMLElement
Dim sURL As String
On Error GoTo Err_Clear
sURL = "https://www.appannie.com/account/login/xxxxxxxxxx"
Set ie = New InternetExplorer
ie.Silent = True
ie.navigate sURL
ie.Visible = True
Do
'Wait until the Browser is loaded
Loop Until ie.readyState = READYSTATE_COMPLETE
Set HTMLDoc = ie.Document
HTMLDoc.all.Email.Value = "xxxxxxxxx#xxx.com"
HTMLDoc.all.Password.Value = "xxxxx"
For Each oHTML_Element In HTMLDoc.getElementById("login-form")
If oHTML_Element.Type = "submit" Then oHTML_Element.Click: Exit For
Next
Dim rating As Variant
Set rating = HTMLDoc.getElementsByClassName("rating-number ng-binding")
Range("A1").Value = rating
'ie.Refresh 'Refresh if required
Err_Clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
End Sub
The code below will let you extract text from first element with class name "rating-number ng-binding" in HTML document. By the way GetElementsByClassName is supported since IE 9.0. I use coding compatible also with older versions in my example.
Dim htmlEle1 as IHTMLElement
For Each htmlEle1 in HTMLDoc.getElementsByTagName("div")
If htmlEle1.className = "rating-number ng-binding" then
Range("A1").Value = htmlEle1.InnerText
Exit For
End if
Next htmlEle1
While Ryszards code should do the trick if you want to use the code you have already written then here is the alterations I believe you need to make.
For Each oHTML_Element In HTMLDoc.getElementById("login-form")
If oHTML_Element.Type = "submit" Then oHTML_Element.Click: Exit For
Next
'Need to wait for page to load before collecting the value
Loop Until ie.readyState = READYSTATE_COMPLETE
Dim rating As IHTMLElement
Set rating = HTMLDoc.getElementsByClassName("rating-number ng-binding")
'Need to get the innerhtml of the element
Range("A1").Value = rating.innerhtml

Getting information from HTML page via VBA

From VBA, I am trying to access to the "username" cell from a web page so that I could type in the appropriate username.
The problem is that in the HTML code from the page we have more than one element with the same name which is "LOGON_USERID" and I can't figure out how to access to the right one.
As you can see on the image "part of the HTML code", the line I'm trying to access to is the highlighted one, but there are also 2 other elements which have the same name above it.
part of the HTML code
I tried lots of different ways (using different methods or variable types etc), but since I'm not familiar with HTML I can't manage to get what I want.
Sub Pum()
Dim ie As New InternetExplorer
'Dim IEDoc As IHTMLElementCollection
Dim IEDoc As HTMLDocument
Dim name As Object
Dim nameList As HTMLInputElement
Dim WRONGS As DispHTMLElementCollection
Dim Elems As HTMLElementCollection
Dim i As Integer
ie.navigate "thewebsiteinquestion"
ie.Visible = False
WaitIE ie
Set IEDoc = ie.document
'MsgBox IEDoc.DocumentElement.
'Elems = IEDoc.getElementsByTagName("INPUT")
MsgBox TypeName(IEDoc.getElementById("LOGON_USERID").all)
Set Elems = IEDoc.getElementById("LOGON_USERID")
'For i = 0 To 5
MsgBox Elems.Length
'Next i
For Each name In Elems.Children
MsgBox name.nodeName
MsgBox name.Attributes
MsgBox name.all
Next
'If ((NameStr Isnot Nothing And (NameStr.Length <> 0)) Then
'If NameStr = "LOGON_USERID" Then
'If TypeName(IEDoc.all("LOGON_USERID")) = "HTMLInputElement" Then
'MsgBox TypeName(IEDoc.all("LOGON_USERID"))
'Set names = IEDoc.all.Item("text")
'TypeName (InputUsernameTextzone)
'Dim Question As IHTMLElement
'Question = InputUsernameTextzone.parentElement
'MsgBox TypeName(InputUsernameTextzone.parentElement.getAttribute("name"))
'InputUsernameTextzone.parentElement
'CELLULE.value = "qtc2464"
WaitIE ie
Set ie = Nothing
Set IEDoc = Nothing
End Sub
I tried two other similar codes using different methods but I still have no results. Hopefully you can help me.
If you need more information, let me know.
The other two input elements are of different type (they are hidden) so you could use querySelector with attribute type=text to find your desired element.
Dim userid As HTMLInputElement
Set userid = IEDoc.querySelector("input[name='LOGON_USERID'][type='text']")
If Not userid Is Nothing Then
' Continue with user id element
Else
MsgBox "LOGON_USERID not found on the page"
End If
I am a newbie at this but if this could help anyone, here's the simplified version of the macro I made :
Sub Access_Puma()
Dim ie As New InternetExplorer
Dim IEDoc As HTMLDocument
Dim userid As HTMLInputElement
Dim userpwd As HTMLInputElement
ie.navigate "thewebsitetoaccess"
ie.Visible = True
WaitIE ie
Set IEDoc = ie.document
Set userid = IEDoc.querySelector("input[name='LOGON_USERID'][type='text']")
If Not userid Is Nothing Then
userid.value = "myusername"
Else
MsgBox "LOGON_USERID not found on the page"
End If
Set userpwd = IEDoc.querySelector("input[name='LOGON_PASSWD'][type='password']")
If Not userpwd Is Nothing Then
userpwd.value = "mypassword"
Else
MsgBox "LOGON_PASSWD not found on the page"
End If
End Sub

VBA Grab data from webpage into variable

I am able to pass the excel values to the website and click it through vba. But it opens up another page with title "Results - Research Randomizer" and I dont know how I retrieve those values inside "Set#1". Can anyone give me some idea to retrieve those values into a variable. My code is
Sub OpenPage()
Const myPageTitle As String = "Research Randomizer Form v4.0"
Const myPageURL As String = "http://www.randomizer.org/form.htm"
Dim NoofSet, NoPSet, RangeBeg, RangeEnd As String
NoofSet = Range("b3").Value
NoPSet = Range("c3").Value
RangeBeg = Range("d3").Value
RangeEnd = Range("e3").Value
Dim myIE As SHDocVw.InternetExplorer
Dim doc As HTMLDocument
Dim PageForm As HTMLFormElement
Dim UserIdBox As HTMLInputElement
Dim PasswordBox As HTMLInputElement
Dim HrangeBeg, HrangeEnd As HTMLInputElement
Dim FormButton As HTMLInputButtonElement
Dim Elem As IHTMLElement
'check if page is already open
Set myIE = GetOpenIEByTitle(myPageTitle, False)
If myIE Is Nothing Then
'page isn't open yet
'create new IE instance
Set myIE = GetNewIE
'make IE window visible
myIE.Visible = True
'load page
If LoadWebPage(myIE, myPageURL) = False Then
'page wasn't loaded
MsgBox "Couldn't open page"
Exit Sub
End If
End If
Do
DoEvents
Loop Until myIE.readyState = READYSTATE_COMPLETE
Set doc = myIE.document
Set PageForm = doc.forms(0)
'Get the User Id textbox
'< input class="TextBox" maxlength="15" name="UserName" size="12">
Set UserIdBox = PageForm.elements("numofsets")
'Set the User Id
UserIdBox.Value = NoofSet
'Get the password textbox
'< input class="TextBox" type="password" maxlength="10" name="Password" size="12">
Set PasswordBox = PageForm.elements("numperset")
'Set the password
PasswordBox.Value = NoPSet
Set HrangeBeg = PageForm.elements("rangebeg")
HrangeBeg.Value = RangeBeg
Set HrangeEnd = PageForm.elements("rangeend")
HrangeEnd.Value = RangeEnd
'Submit the form (like clicking the 'Submit' button) to navigate to next page
PageForm.Button.Click
'Wait for the new page to load
Do
DoEvents
Loop Until myIE.readyState = READYSTATE_COMPLETE
myIE.Visible = True
'Working fine till here
'Need to pull the data from the 2nd webisite
End Sub
'returns new instance of Internet Explorer
Function GetNewIE() As SHDocVw.InternetExplorer
'create new IE instance
Set GetNewIE = New SHDocVw.InternetExplorer
'start with a blank page
GetNewIE.Navigate2 "about:Blank"
End Function
'loads a web page and returns True or False depending on
'whether the page could be loaded or not
Function LoadWebPage(i_IE As SHDocVw.InternetExplorer, _
i_URL As String) As Boolean
With i_IE
'open page
.navigate i_URL
'wait until IE finished loading the page
Do While .readyState <> READYSTATE_COMPLETE
Application.Wait Now + TimeValue("0:00:01")
Loop
'check if page could be loaded
If .document.URL = i_URL Then
LoadWebPage = True
End If
End With
End Function
'finds an open IE site by checking the URL
Function GetOpenIEByURL(ByVal i_URL As String) As SHDocVw.InternetExplorer
Dim objShellWindows As New SHDocVw.ShellWindows
'ignore errors when accessing the document property
On Error Resume Next
'loop over all Shell-Windows
For Each GetOpenIEByURL In objShellWindows
'if the document is of type HTMLDocument, it is an IE window
If TypeName(GetOpenIEByURL.document) = "HTMLDocument" Then
'check the URL
If GetOpenIEByURL.document.URL = i_URL Then
'leave, we found the right window
Exit Function
End If
End If
Next
End Function
'finds an open IE site by checking the title
Function GetOpenIEByTitle(i_Title As String, _
Optional ByVal i_ExactMatch As Boolean = True) As SHDocVw.InternetExplorer
Dim objShellWindows As New SHDocVw.ShellWindows
If i_ExactMatch = False Then i_Title = "*" & i_Title & "*"
'ignore errors when accessing the document property
On Error Resume Next
'loop over all Shell-Windows
For Each GetOpenIEByTitle In objShellWindows
'if the document is of type HTMLDocument, it is an IE window
If TypeName(GetOpenIEByTitle.document) = "HTMLDocument" Then
'check the title
If GetOpenIEByTitle.document.Title Like i_Title Then
'leave, we found the right window
Exit Function
End If
End If
Next
End Function
This code will find and identify the open "Results" window and then assign the source code behind it to a variable (my_var). You can then extract what you want from the variable.
' Find the open instance of IE that contains the "Results"
Set objShell = CreateObject("Shell.Application")
IE_count = objShell.Windows.Count
For x = 0 To (IE_count - 1)
On Error Resume Next
my_url = objShell.Windows(x).document.Location
my_title = objShell.Windows(x).document.Title
If my_title Like "Results - Research Randomizer" Then
Set ie = objShell.Windows(x)
Exit For
Else
End If
Next
my_var = ie.document.body.innerhtml