Various errors with VBA Loop to pull HTML data - vba

I've been getting various errors with the below VBA code (most recent error is Run-time error '70': permission denied). Basically the code/worksheet connects to an intranet IE database of customers, searches customer activity and imports any activity to the worksheet (will eventually use the activity for reporting). Here's where I run into the errors, depending on the length of time I'm searching I sometimes have multiple pages of activity to pull which requires clicking the "next" button and pull the data from each page until there is no longer a "next" button (no more activity). The loop I have set up will pull from the first page, click the "next" button then sometimes pull from the second sheet but then it trips the error. So I think the error has something to do with the loading of the pages but I've added pauses to allow for loading but still run into the same errors. I'm really stuck on this and unfortunately I can't move forward with the project until I can solve this issue.
Here is the code snippet:
Dim TDelements As IHTMLElementCollection
Dim TDelement As HTMLTableCell
Dim r As Long, i As Long
Dim e As Object
Set TDelements = IE.document.getElementsByTagName("tr")
r = 0
For i = 1 To 1
Application.Wait Now + TimeValue("00:00:03")
For Each TDelement In TDelements
If TDelement.className = "searchActivityResultsContent" Then
Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
r = r + 1
ElseIf TDelement.className = "searchActivityResultsContent" Then
Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
r = r + 1
End If
Next
Application.Wait Now + TimeValue("00:00:02")
Set elems = IE.document.getElementsByTagName("input")
For Each e In elems
If e.Value = "Next Results" Then
e.Click
i = 0
Exit For
End If
Next e
Next i
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
IE.Quit
End Sub
Any help/suggestions would be very much appreciated. Thank you!

Looking at your code:
'getting any TD elements here
Set TDelements = IE.document.getElementsByTagName("tr")
'waiting here....
Application.Wait Now + TimeValue("00:00:03")
'now trying to use items in TDelements...
For Each TDelement In TDelements
'...
Next
Are you waiting for the page to load when you use Application.Wait ?
If Yes then you should know that TDelements isn't dynamic - it won't update itself as new TD elements are loaded: it's just a snapshot of the elements which were present when you called getElementsByTagName("tr"). So call that after the wait.

Related

Return Address from Google term search to excel using VBA

I am familiar with StackOverflow but have just recently signed up. I am trying to search a Hotel on google and return the address in Excel using VBA. Below is a photo of what Information I am trying to return from Google. From my research, I was able to find a VBA that allowed me to return the Results stats.
Would it be possible to modify my code and return the box at the top of my google search?
I would really appreciate your help! Below is the VBA I am using to return search results.
Sample Image - Red Roof Inn & Address
Sub SearchGoogle()
Dim ie As Object
Dim form As Variant
Dim button As Variant
Dim LR As Integer
Dim var As String
Dim var1 As Object
LR = Cells(Rows.Count, 1).End(xlUp).Row
For x = 2 To LR
var = Cells(x, 1).Value
Set ie = CreateObject("internetexplorer.application")
ie.Visible = True
With ie
.Visible = True
.navigate "http://www.google.co.in"
While Not .readyState = READYSTATE_COMPLETE
Wend
End With
'Wait some to time for loading the page
While ie.Busy
DoEvents
Wend
Application.Wait (Now + TimeValue("0:00:02"))
ie.document.getElementById("lst-ib").Value = var
'Here we are clicking on search Button
Set form = ie.document.getElementsByTagName("form")
Application.Wait (Now + TimeValue("0:00:02"))
Set button = form(0).onsubmit
form(0).submit
'wait for page to load
While ie.Busy
DoEvents
Wend
Application.Wait (Now + TimeValue("0:00:02"))
Set var1 = ie.document.getElementById("resultStats")
Cells(x, 2).Value = var1.innerText
ie.Quit
Set ie = Nothing
Next x
End Sub
Right now your code loads the page and then loads the value of the resultStats element.
So the section of your code that you will need to alter is:
Set var1 = ie.document.getElementById("resultStats")
Cells(x, 2).Value = var1.innerText
The first step to your problem is to understand the DOM of the HTML page you are attempting to use, in this case Google. I would suggest using a browser to navigate the DOM as it would give you a good idea of what the whole page is doing.
If you are aiming to do this on a macro basis you will need a path through the DOM that will always take you where you want to go. I would suggest having two pages with different searches open so that you can check you hypothesis as you go.
For example the boxes that you refer to seem to be located in a class called kp-header from knowing this you can build out your path through the DOM to return the text value displayed on screen. Again you will need to do your own investigations to find the best stating point for your search as kp-header was just the first potently helpful result I could find.
Although please note that depending on the speed you are loading these webpages you may hit a limit from google as they discourage scraping. What would be a better option to avoid these limits and to avoid yourself having to investigate all of google's DOM would be to try and incorporate one of google's API's

Excel VBA loop to input data in webpage

I have gone through many articles and able to input data in to webpage from excel. Now I am trying to use a loop because I have to entry data for 10 to 50 times in the same page.
Here is the code
Sub vfcp()
Set objIE = CreateObject("InternetExplorer.Application")
objIE.Top = 100
objIE.Left = 100
objIE.Width = 800
objIE.Height = 800
objIE.AddressBar = 1
objIE.StatusBar = 1
objIE.Toolbar = 1
objIE.Visible = True
Dim Input
Range("A2").Select
objIE.Navigate ("https://www.askebsa.dol.gov/VFCPCalculator/WebCalculator.aspx")
Do Until Selection.Offset(0, 0).Value = ""
If objIE.ReadyState = 4 Then
Input = Selection.Offset(0, 2).Value
objIE.document.GetElementByID("_ctl0_MainContent_txtPrincipal").Value = Input
objIE.document.GetElementByID("_ctl0_MainContent_cmdCalculate").Click
Selection.Offset(1, 0).Select
End If
Loop
End Sub
The problems in this code is, I have a list of 10 date. the loop inputs the last date of the list. I am trying to input the first date and click submit, then wait for the page to load and input the second date, and stop the loop if there is an empty cell.
Where is the code to enter the dates? You cannot click without them.
"then wait for the page to load and input the second date"
you can achieve this by inserting the following between your End If and Loop lines.
'Allows IE to load
While objIE.ReadyState <> 4
DoEvents
Wend
This shouldn't be too hard. I went to that URL, hit F12 to see the code behind the site, and started searching for the appropriate ID names ('Principal:', 'Loss Date:', etc). That's about it. Run the script to see a working version. Oh, yeah, set a reference to 'Microsoft Internet Controls' before you run the code.
Sub TryThis()
Dim ie As SHDocVw.InternetExplorer
Set ie = New SHDocVw.InternetExplorer
ie.Visible = True
ie.Navigate "https://www.askebsa.dol.gov/VFCPCalculator/WebCalculator.aspx"
Do
DoEvents
Loop Until ie.readystate = 4
'ie READYSTATE has 5 different status codes, here we are using status 4:
Call ie.Document.GetElementByID("_ctl0_MainContent_txtPrincipal").SetAttribute("value", "100000")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtLossDateMonth").SetAttribute("value", "01")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtLossDateDay").SetAttribute("value", "01")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtLossDateYear").SetAttribute("value", "2016")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtRecoveryDateMonth").SetAttribute("value", "01")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtRecoveryDateDay").SetAttribute("value", "01")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtRecoveryDateYear").SetAttribute("value", "2017")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtFinalPaymentMonth").SetAttribute("value", "12")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtFinalPaymentDay").SetAttribute("value", "30")
Call ie.Document.GetElementByID("_ctl0_MainContent_txtFinalPaymentYear").SetAttribute("value", "2017")
'to find the "<input……/>" tag hyper link and to click the button
Set AllInputs = ie.Document.getelementsbytagname("input") 'you can use any tagname as you wish
For Each hyper_link In AllInputs
If hyper_link.Name = "_ctl0:MainContent:cmdCalculate" Then 'you can use .name, .id etc
hyper_link.Click
Exit For
End If
Next
Do
DoEvents
Loop Until ie.readystate = 3
Do
DoEvents
Loop Until ie.readystate = 4
End Sub
If you want to scrape the results, refresh the URL and do the same as I described above: F12, search for the relevant IDs and/or names, and pull those into a Worksheet in Excel. I'll leave it to you to figure out that part. It will be a good learning experience!!!

VBA Internet Explorer dropdown list trouble

I'm trying to scrape some data using VBA via Excel. I'm pretty rusty with VBA but I'm confident in my google-fu, 5+ hours in and no luck yet. I have seen many ways to work with drop-down menus but none seem to work with this one. I have successfully navigated to https://www.kbb.com/used-cars/ but I cannot work with the "year" drop-down. Maybe I am not using the correct id/name? Any advice is appreciated, let me know if I left any important info out.
Code:
Sub WebForumEntry()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.Top = 0
IE.Left = 0
IE.Width = 800
IE.Height = 600
IE.AddressBar = 0
IE.StatusBar = 0
IE.Toolbar = 0
IE.Navigate "http://www.kbb.com"
IE.Visible = True
Do
DoEvents
Loop Until IE.ReadyState = 4
'Click on the "Used Car Values" link
Set AllHyperlinks = IE.document.getelementsbytagname("A")
For Each hyper_link In AllHyperlinks
If hyper_link.innertext = "Used Car Prices" Then
hyper_link.Click
Exit For
End If
Next
'Select model year"
Set e = IE.document.getElementbyid("yearDropdown")
Dim o
For Each o In e.Options
If o.Value = "2012" Then
o.Selected = True
Exit For
End If
Next
End Sub
In the HTML there are no Option tags for you to select:
<div class="form-group">
<select id="yearDropdown" name="yearId" class="dropdown-one year-dropdown" data-default-option="Year" data-preselected-value=""></select>
</div>
The years are generated dynamically depending on whether you choose new/ used etc. So you can't select something that doesn't exist - perhaps you can directly set the value e.g.:
IE.document.getElementbyid("yearDropdown").value = "2012"
Edit
Note there is an issue in your original code:
'Click on the "Used Car Values" link
Set AllHyperlinks = IE.document.getelementsbytagname("A")
For Each hyper_link In AllHyperlinks
If hyper_link.innertext = "Used Car Prices" Then
hyper_link.Click
Exit For
End If
Next
'Select model year"
Set e = IE.document.getElementbyid("yearDropdown") '<-- error here if page not loaded per hyper_link.Click
When you call hyper_link.Click it will load another page (where the dropdowns are) and then per the Exit For the next command to run is Set e = IE... which will try and access an element that doesn't exist yet because the page is still loading per the hyper_link.Click. To work-around this you can just put in a pause like below:
Application.Wait Now + TimeValue("0:00:04")
It's a bit of a hack - and there may be better ways to solve it - but it will allow you to set the dropdown per my first suggestion :
Full code (re-write of yours):
Option Explicit
Sub WebForumEntry()
Dim IE As Object
Dim AllHyperLinks As Object
Dim hyper_link As Object
Dim e As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.Top = 0
IE.Left = 0
IE.Width = 800
IE.Height = 600
IE.AddressBar = 0
IE.StatusBar = 0
IE.Toolbar = 0
IE.Navigate "http://www.kbb.com"
IE.Visible = True
' wait for page to load
Do
DoEvents
Loop Until IE.ReadyState = 4
'Click on the "Used Car Values" link
Set AllHyperLinks = IE.document.getelementsbytagname("A")
For Each hyper_link In AllHyperLinks
If hyper_link.innertext = "Used Car Prices" Then
hyper_link.Click
Exit For
End If
Next
'wait 4 seconds - so page fully loads
Application.Wait Now + TimeValue("0:00:04")
'Select model year"
Set e = IE.document.getElementbyid("yearDropdown")
' set a year
e.Value = "2016"
End Sub
...try and access an element that doesn't exist yet because the page is still loading...just put in a pause
Fantastic, thank you Robin!

Importing specific web data to excel using VBA

I'm very much beginner to the VBA coding scene (web scripting is more my thing) but I have an excel based program I need to create that will import data from a intranet web based application into a spreadsheet. Here's the gist of what I'm looking to set up...
In the spreadsheet the user will enter the following info: username, password, list of customer account numbers and a date range. The user will then click a "command button" that will make the following happen:
Open web based program, login (based on login/password typed into spreadsheet) and navigate to the account search screen.
Enter first customer account number into search field and click the "search" button to navigate to the specific customer account.
Navigate to the "search activity" screen, enter the date range and click the "search activity button.
Pull the data from a specific column of the activity table and import the data to the spreadsheet.
If there are multiple pages of data there will be a "Next Results" button, there should be a loop to click the next results button (if it exists) and pull the same column of data from each page until the button no longer exists (no more data).
Once there are no more pages of data (or if there is only one page) the macro will loop back and navigate to the account search screen and perform the same operations for each account in the list of accounts typed into the spreadsheet until there are no other accounts.
Once completed (all data successfully imported to the spreadsheet) it should close the IE window.
It's a little complicated and I realize excel/vba is definitely not the best solution for performing these functions but unfortunately it's what I have to use in this instance. I've been able to piece together some VBA that does almost everything above, the problem I'm having is looping through the activity pages and pulling the data just will not work (get a wide range of errors that only confuse me more), sometimes it will pull data from the first sheet, click the "next results" button, get to the next page and throw an error or even get through two or three pages and throw an error. It doesn't make a lot of sense but the most common error is "permission denied". Also this code currently only pulls the data from one account, I was hoping once I got it working for one account it would be simple to create a loop of the entire code to have it go down the list of account numbers and do the same for each until completed. I've been stuck on this for a number of weeks and I'm really ready to toss out the whole thing and start from scratch, any help would be very very appreciated!
Below is the code I have so far...
Private Sub CommandButton1_Click()
' open IE, navigate to the desired page and loop until fully loaded
Set IE = New InternetExplorerMedium
my_url = "https://customerinfo/pages/login.jsp"
my_url2 = "https://customerinfo/pages/searchCustomer.jsp"
my_url3 = "https://customerinfo/pages/searchAccountActivity.jsp"
With IE
.Visible = True
.navigate my_url
Do Until Not .Busy And .readyState = 4
DoEvents
Loop
End With
' Input the userid and password
IE.document.getElementById("userId").Value = [B2]
IE.document.getElementById("password").Value = [B3]
' Click the "Login" button
IE.document.getElementById("action").Click
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
' Navigate to Search screen
With IE
.navigate my_url2
Do Until Not .Busy And .readyState = 4
DoEvents
Loop
End With
' Input the account number & click search
IE.document.getElementById("accountNumber").Value = [B5]
IE.document.getElementById("action").Click
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
With IE
.navigate my_url3
Do Until Not .Busy And .readyState = 4
DoEvents
Loop
End With
'Input search criteria
IE.document.getElementById("store").Value = [C7]
IE.document.getElementById("dateFromMonth").Value = [C10]
IE.document.getElementById("dateFromDay").Value = [B11]
IE.document.getElementById("dateFromYear").Value = [B12]
IE.document.getElementById("timeFromHour").Value = [B20]
IE.document.getElementById("timeFromMinute").Value = [B21]
IE.document.getElementById("dateToMonth").Value = [C15]
IE.document.getElementById("dateToDay").Value = [B16]
IE.document.getElementById("dateToYear").Value = [B17]
IE.document.getElementById("timeToHour").Value = [B24]
IE.document.getElementById("timeToMinute").Value = [B25]
IE.document.getElementById("action").Click
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
'Pulls data from activity search
Dim TDelements As IHTMLElementCollection
Dim TDelement As HTMLTableCell
Dim r As Long, i As Long
Dim e As Object
Application.Wait Now + TimeValue("00:00:05")
Set TDelements = IE.document.getElementsByTagName("tr")
r = 0
For i = 1 To 1
Application.Wait Now + TimeValue("00:00:03")
For Each TDelement In TDelements
If TDelement.className = "searchActivityResultsOldContent" Then
Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
r = r + 1
ElseIf TDelement.className = "searchActivityResultsNewContent" Then
Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
r = r + 1
End If
Next
Application.Wait Now + TimeValue("00:00:02")
Set elems = IE.document.getElementsByTagName("input")
For Each e In elems
If e.Value = "Next Results" Then
e.Click
i = 0
Exit For
End If
Next e
Next i
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
IE.Quit
End Sub
So, what is happening after you've clicked on "Next..." element? Let me describe an issue I encountered. Assume the code flow as follows:
Create IE instance, and navigate to some URL, e. g. first search results page.
Make a check if the page is loaded and ready. Wait for it.
Create the DispHTMLElementCollection collection of the target elements, retrieved by .document.getElementsByTagName(), etc..
Loop through the elements of the collection, do some stuff.
Click on the "Next ..." element. The issue is that in some cases the next page doesn't start downloading immediately after click due to some JS or XHR processing.
Make a conventional check if the next page is loaded and ready. This check just allows the further code execution without any delay, since downloading of the next page has not been started immediately after click, and the current existing page is determined as next page downloaded and ready, by mistake. Simple several secs delays doesn't provide reliable way to get the ready page.
Again, create the DispHTMLElementCollection collection of the elements from the existing page, instead of the next page, by mistake.
Loop through the elements of the created collection. While the loop in progress, the next page starts downloading. The collection still contains the references to the objects, but actually the page with that objects has been unloaded. Thereby either attempt to access to the element of the unloaded page or due to document object is unresponsive, the operation gives "permission denied" errors.
My clue is to avoid clicking on "Next...", try to read the next page URL from .href property of the "Next..." anchor <a> element, and invoke IE.navigate to that URL, then check the page readiness.
Take a look at the example implementing that approach.
IMO the most efficient way is to use XHR, like this, this and this.

Refresh the webpage, while the webpage is not loaded properly in VBA excel

I have created a program that extracts data from website and keeps in a spreadsheet. But the major problem I am encountering is the hanging of Internet Explorer.
With ie
.Visible = True
.Navigate urll
Do While .readyState <> 4
Application.StatusBar = "Opening Page : " & i & ", Please wait..."
DoEvents
Loop
Set html = .Document
End With
The loop Do While sometimes sticks and never ends as the internet explorer is not able to load properly and never comes to readystate of 4. In this case I have to either refresh the page manually (keeping visibility of ie true) or I have to stop the program, and do some updates to program (locations of source and destination of data). This is pretty time consuming if every 10th webpage is keeping the loop open.
I have one solution, i.e. during the time loop goes on the program should check about the time elapsed during the loop execution, if the loop continues for more than 50 secs, the program should suspend the current loop and start again by refreshing page. (Please let me know if you have a better logic).
I am not able to do a correct coding for this job. Can anyone solve this...
Try this (UNTESTED)
What this does is increments a variable and checks for the number of time the loop was called.
Dim nCount As Long
Sub Sample()
'
'~~> Rest of the code
'
With ie
.Visible = True
.Navigate urll
Do While .readyState <> 4
Application.StatusBar = "Opening Page : " & i & ", Please wait..."
Wait 2 '<~~ Wait for 2 Seconds
If nCount > 25 Then
'~~> Approx 50 seconds have elapsed
End If
Loop
Set HTML = .Document
End With
'
'~~> Rest of the code
'
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
nCount = nCount + 1
End Sub