Check if website checkbox in checked with excel VBA - html

I am trying to make a VBA that can read the HTML and checked if a specific check box is checked and write either check or unchecked in a cell. But I am having difficulties with VBA as I do not use it as often, any advise will be appreciated.
HTML
<input id="foo1" type="checkbox" name="Device" value="iPad"
checked="checked">
VBA
Sub getValue()
Dim IE As Object: Set IE =
CreateObject("InternetExplorer.Application")
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim Country As String
With IE
.Visible = False
.navigate ws.Range("A3").Value
Do
DoEvents
Loop Until .readyState = 4
End With
Set oShell = CreateObject("WScript.Shell")
Dim document
document.getElementById("checkBox(iPad)")
Item(0).Checked = True
End Sub

Try
Debug.Print ie.document.querySelector("#foo1").getAttribute("checked") ="checked"
I am not sure, without an URL to test with whether there is .Checked property you can evaluate for True ( ie.document.querySelector("#foo1").Checked)
Without more HTML hard to say if this will be able to access the required element. There may be forms/iframes/frames to negotiate.

This can also be done with looping all the input elements on the website until you find the one with the right name, this will of course be slower then a querySelector, but can be usefull if you need to change multiple input elements.
Dim objCollection as Object
Set objCollection = ie.Document.getElementsByTagName("input")
i = 0
'Loop through all elements and find the checkbox
While i < objCollection.Length
If objCollection(i).Name = "Device" Then
objCollection(i).Checked = False
End If
i = i + 1
Wend
If you only have 1 checkbox i would no doubt go with a querySelector as #QHarr

Related

How to look up HTML input name then assign a value in VBA?

I am fairly new to VBA and trying to log in to a website. I have gotten the code to get me to the website then look up the input names for the username and password elements. This was important because everytime the page opens the names are slightly different so I can't just inspect the element name and use that as a fixed value.
The username input name always starts with "txt_1_" then some numbers and the password goes "txt_2_" and some numbers. That is the reason I have the if statement that looks for the names similar to those.
The error I am currently receiving is "Run-time error '438': Object doesn't support this property or method"
Below is what I have so far:
Sub login()
Const Url$ = "examplewebsite.com"
Dim HTMLDoc As HTMLDocument
Dim oHTML_Element As IHTMLElement
Dim ie As Object
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Navigate Url
ieBusy ie
.Visible = True
Set HTMLDoc = ie.Document
Dim ID1 As String, ID2 As String
For Each oHTML_Element In HTMLDoc.getElementsByTagName("input")
If oHTML_Element.Name Like "txt_1*" Then ID1 = oHTML_Element.Name
If oHTML_Element.Name Like "txt_2*" Then ID2 = oHTML_Element.Name
Next
Debug.Print ID1
Debug.Print ID2
Dim oLogin As Object, oPassword As Object
Set oLogin = .Document.getElementsByTagName(ID1)
Set oPassword = .Document.getElementsByTagName(ID2)
oLogin.Value = "MyUsername"
oPassword.Value = "MyPassword"
.Document.forms(0).submit
End With
End Sub
Sub ieBusy(ie As Object)
Do While ie.Busy Or ie.ReadyState < 4
DoEvents
Loop
End Sub
Issue
getElementsByTagName returns a collection of elements with the given tag name. So for your For loop it works for you to loop all the elements with the tagname of input.
However, when you are trying to assign oLogin and oPassword using the same getElementsByTagName it won't work, as you are passing a name attribute to it and not a tagname.
Solution
Using your code you can simply assign oLogin and oPassword in your for loop.
For Each oHTML_Element In HTMLDoc.getElementsByTagName("input")
If oHTML_Element.Name Like "txt_1*" Then set oLogin = oHTML_Element
If oHTML_Element.Name Like "txt_2*" Then set oPassword = oHTML_Element
Next
Another solution could be by using the attribute selector, specifically the starts with symbol. That pattern looks like [attribute^="value"]. So in your case input[name^="txt_1"] and input[name^="txt_2"].
This would then be used with querySelector instead of getElementsByTagName. This means you wouldn't have to loop either.
set oLogin = HTMLDoc.querySelector("input[name^=""txt_1""]")
set oPassowrd = HTMLDoc.querySelector("input[name^=""txt_2""]")
A few extra notes
You are using Late Binding for the Microsoft Internet Controls library — really you should use it early binding by creating a reference to the library.
Try to avoid using Systems Hungarian notation with your variable names — adding o before password and login gets really confusing for others to read (Read This Article).
Avoid writing your declarations on a single line. This is easy to write errors, and also is harder to read.

Website scraping: website search box has no value

I am trying to crosscheck a large body of data with a specific website (https://icis.corp.delaware.gov/Ecorp/EntitySearch/NameSearch.aspx).
The goal is to search for many company names based on a larger list in Excel to get their founding dates. For now I am starting out with a single name to get it running. I am having trouble in my main code as there is no inherent input value in the HTML code:
<input name="ctl00$ContentPlaceHolder1$frmEntityName" type="text" id="ctl00_ContentPlaceHolder1_frmEntityName" tabindex="4" size="30" maxlength="120" class="txtNormal" onkeyup="KeyEvent1(this.id)">
I tried the following:
Sub click_search()
Dim i As SHDocVw.InternetExplorer
Set i = New InternetExplorer
i.Visible = True
i.Navigate "https://icis.corp.delaware.gov/Ecorp/EntitySearch/NameSearch.aspx"
Do While i.ReadyState <> READYSTATE_COMPLETE
Loop
Dim idoc As MSHTML.HTMLDocument
Set idock = i.Document
idoc.getElementsByTagName("input").Item("ctl00$ContentPlaceHolder1$frmEntityName").Value = "10X Genomics Inc"
End Sub
The problem I believe is the HTML code does not have inherent value = "" to begin with but it only comes up in the HTML code after you write it in.
How do I fix this and furthermore then click the search button?
The error is
"Object variable or With block variable not set"
Always use Option Explicit at the top of every VBA code file.
If the webpage in question contains ids for the elements you are interested in, use getElementById() to access them. This code works, however it does not find any records.
Option Explicit
Sub click_search()
Dim i As SHDocVw.InternetExplorer
Dim idoc As MSHTML.HTMLDocument
Set i = New InternetExplorer
i.Visible = True
i.Navigate "https://icis.corp.delaware.gov/Ecorp/EntitySearch/NameSearch.aspx"
Do While i.ReadyState <> READYSTATE_COMPLETE
Loop
Set idoc = i.Document
idoc.getElementById("ctl00_ContentPlaceHolder1_frmEntityName").Value = "10X Genomics Inc"
idoc.getElementById("ctl00_ContentPlaceHolder1_frmFileNumber").Value = "1"
idoc.getElementById("ctl00_ContentPlaceHolder1_btnSubmit").Click
End Sub

VBA Web search button - GetElementsbyClassName

I have a problem with the VBA code.
I would like to open this website: https://www.tnt.com/express/en_us/site/tracking.html and in Shipment numbers search box I would like to put active cells from Excel file. At the beginning I tried to put only a specific text for example: "777777".
I wrote the below code but unfortunately, the search button is empty and there is no error. I tried everything and I have no idea what should I change in my code.
Any clues? Thank you in advance.
HTML:
<input class="__c-form-field__text ng-touched ng-dirty ng-invalid" formcontrolname="query" pbconvertnewlinestocommasonpaste="" pbsearchhistorynavigation="" shamselectalltextonfocus="" type="search">
VBA:
Sub TNT2_tracker()
Dim objIE As InternetExplorer
Dim aEle As HTMLLinkElement
Dim y As Integer
Dim result As String
Set objIE = New InternetExplorer
objIE.Visible = True
objIE.navigate "https://www.tnt.com/express/en_us/site/tracking.html"
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
Dim webpageelement As Object
For Each webpageelement In objIE.document.getElementsByClassName("input")
If webpageelement.Class = "__c-form-field__text ng-pristine ng-invalid ng-touched" Then
webpageelement.Value = "777"
End If
Next webpageelement
End Sub
You could use the querySelector + class name to find an element.
something like
'Find the input box
objIE.document.querySelector("input.__c-form-field__text").value = "test"
'Find the search button and do a click
objIE.document.querySelector("button.__c-btn").Click
No need to loop through elements. Unless the site allows you to search multiple tracking numbers at the same time.
It seems automating this page is a litte tricky. If you change the value of the input field it doesn' t work. Nothing happen by clicking the submit button.
A look in the dom inspector shows several events for the input field. I checked them out and it seems we need to paste the value over the clipboard by trigger the paste event of the shipping field.
In order for this to work without Internet Explorer prompting, its security settings for the Internet zone must be set to allow pasting from the clipboard. I'm using a German version of IE, so I have problems explaining how to find the setting.
This macro works for me:
Sub TNT2_tracker()
Dim browser As Object
Dim url As String
Dim nodeDivWithInputField As Object
Dim nodeInputShipmentNumber As Object
Dim textToClipboard As Object
'Dataobject by late binding to use the clipboard
Set textToClipboard = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
url = "https://www.tnt.com/express/en_us/site/tracking.html"
'Initialize Internet Explorer, set visibility,
'call URL and wait until page is fully loaded
Set browser = CreateObject("internetexplorer.application")
browser.Visible = True
browser.navigate url
Do Until browser.ReadyState = 4: DoEvents: Loop
'Manual break for loading the page complitly
'Application.Wait (Now + TimeSerial(pause_hours, pause_minutes, pause_seconds))
Application.Wait (Now + TimeSerial(0, 0, 3))
'Get div element with input field for shipment number
Set nodeDivWithInputField = browser.Document.getElementsByClassName("pb-search-form-input-group")(0)
If Not nodeDivWithInputField Is Nothing Then
'If we got the div element ...
'First child element is the input field
Set nodeInputShipmentNumber = nodeDivWithInputField.FirstChild
'Put shipment number to clipboard
textToClipboard.setText "7777777"
textToClipboard.PutInClipboard
'Insert value by trigger paste event of the input field
Call TriggerEvent(browser.Document, nodeInputShipmentNumber, "paste")
'Click button
browser.Document.getElementsByClassName("__c-btn")(0).Click
Else
MsgBox "No input field for shipment number found."
End If
End Sub
And this function to trigger a html event:
Private Sub TriggerEvent(htmlDocument As Object, htmlElementWithEvent As Object, eventType As String)
Dim theEvent As Object
htmlElementWithEvent.Focus
Set theEvent = htmlDocument.createEvent("HTMLEvents")
theEvent.initEvent eventType, True, False
htmlElementWithEvent.dispatchEvent theEvent
End Sub
As #Stavros Jon alludes to..... there is a browserless way using xhr GET request via API. It returns json and thus you ideally need to use a json parser to handle the response.
I use jsonconverter.bas as the json parser to handle the response. Download raw code from here and add to standard module called JsonConverter . You then need to go VBE > Tools > References > Add reference to Microsoft Scripting Runtime. Remove the top Attribute line from the copied code.
Example request with dummy tracking number (deliberately passed as string):
Option Explicit
Public Sub TntTracking()
Dim json As Object, ws As Worksheet, trackingNumber As String
trackingNumber = "1234567" 'test input value. Currently this is not a valid input but is for demo.
Set ws = ThisWorkbook.Worksheets("Sheet1") 'for later use if writing something specific out
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://www.tnt.com/api/v3/shipment?con=" & trackingNumber & "&searchType=CON&locale=en_US&channel=OPENTRACK", False
.send
Set json = JsonConverter.ParseJson(.responseText)
End With
'do something with results
Debug.Print json("tracker.output")("notFound").Count > 0
Debug.Print JsonConverter.ConvertToJson(json("tracker.output")("notFound"))
End Sub

How can I pull data from website using vba

I am new at vba coding to pull data from website so generally, I use this code to connect and check item to pull data from website but this code cannot check data via watch in vba with my firm webapp. it show nothing when I add watch to the class so what should I do.HTML Code from my firm webapp 1
HTML Code from my firm webapp 2
Sub Connect_web()
Dim ie As InternetExplorer
Dim doc As HTMLdocument
Dim ele As IHTMLElement
Dim col As IHTMLElementCollection
Dim ele_tmp As IHTMLElement
Set ie = New InternetExplorer
URL = "" ' Cannot provide
ie.Visible = True
ie.navigate URL
Do While ie.readyState <> READYSTATE_COMPLETE
Application.StatusBar = "Loading Page..."
DoEvents
End If
Loop
Set doc = ie.Document
Set ele = doc.getElementByClassName("GDB3EHGDHLC")
end sub
Let's start with four things:
1) Instead of .Navigate use .Navigate2
2) Use a proper wait
While ie.Busy Or ie.readyState < 4: DoEvents: Wend
3) Correct the syntax of your Set ele line. You are using ByClassNamewhich returns a collection and therefore is plural. You are missing the s at the end of element.
As you have declared ele as singular (element), perhaps first set the collection into a separate variable and index into that collection.
Dim eles As Object, ele As Object
Set eles = doc.getElementsByClassName("GDB3EHGDHLC")
Set ele = eles(0)
4) You should always use id over other attributes, if possible, as id is usually quicker for retrieval. There is an id against that class name in your image (highlighted element). I am not going to try and type it all out. Please share your HTML using the snippet tool, by editing your question, so we can relate to your html in answer easily.
Set ele = doc.getElementById("gwt-debug-restOfIdStringGoesHere")

Get value from web document input element with VBA

I am having difficult to retrieve value 300 from the input named points.
Here's my HTML and VBA code.
HTML:
<td id="myPower_val_9" style="visibility: visible;">
<input type="text" disabled="disabled" value="300" name="points"></input>
</td>
VBA:
Dim ie As Object
Dim myPoints As String
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 0
.navigate "www.example.com"
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByTagName("td")(0).getElementById("myPoints").innerText)
Range("A1").Value = myPoints
HTML Code
I'd try working out the code that manipulates the Document Object Model (DOM) in javascript in a web browser so you can make use of better web based debugging tools.
There are several issues here that a console or debugger could help out with:
You want to get the element ID myPoints but in HTML it's just called points
You want to get the element by ID, but you've only set the name property -
As long as name is unique to the element, you don't need to search for a td first
As you can see from <input></input>, input elements do not have innerText (the text inside the ><). Instead they have a value attribute
The element exposes it's attributes and other data through the properties on the object itself. So you can check the input's value by just looking at .value
Here's a javascript example of what you're trying to do:
var value = document.getElementsByName("points")[0].value;
console.log(value);
<input type="text" disabled="disabled" value="300" name="points" />
Open the console (F12), and you should see 300
VBA
To convert it to VBA code for Excel, just make sure you uses parentheses () for VB arrays instead of square brackets [] for JS arrays:
myPoints = Trim(Doc.getElementsByName("points")(0).Value)
That should work just fine.
References
Since I'm not sure at what point you're failing in VB, also make sure you have all the proper web references in place in your VBA script.
Go to Tools > References > and add "Microsoft HTML Object Library" and "Microsoft Internet Controls":
Demo
I created a demo in plunker so there would be a live site to go against instead of example.com.
Paste the following code into excel and everything should work fine:
Public Sub GetValueFromBrowser()
Dim ie As Object
Dim url As String
Dim myPoints As String
url = "http://run.plnkr.co/plunks/6UTb9kHRZ363Ivhh2BPE/"
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 0
.navigate url
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByName("points")(0).Value)
Range("A1").Value = myPoints
End Sub
Output:
CSS selector:
Use a CSS selector to get the element of input[name='points']
You don't show enough HTML to know if this is the only on the page. The above says elements with input tag having attribute name whose value is 'points'
CSS query:
VBA:
You apply the CSS selector with .querySelector method of document for a single element; .querySelectorAll for a nodeList of all matching elements i.e. if there is more than one on the page and you get the one of interest by index.
Debug.Print ie.document.querySelector("input[name='points']").getAttribute("value")
You need to use .getAttribute("name of attribute") to get an attributes value. In your case .getAttribute("value") will return 300.
Dim ie As Object
Dim myPoints As String
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 1
.navigate "website URL"
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
myPoints = Trim(Doc.getElementsByTagName("td")(0).getElementsByTagName("input")(0).getAttribute("value"))
Range("A1").Value = myPoints
Just on a side note. I don't know much about HTML and maybe someone can elaborate on this more. But if you want to test that HTML code you need to add in the < table> < tr> tags.
Something like this:
<table>
<tr>
<td id="myPower_val_9" style="visibility: visible;">
<input type="text" disabled="disabled" value="300" name="points"></input>
</td>
</tr>
</table>