I am doing IE automation using VBA (Basically I open IE and goto the specific URL from the sheet and then login using credentials from the sheet and then extract data from the webpage to excel) This has to happen for 20 websites so I added for loop and it works fine.
What I want is, in case of any error occurs with in the loop then loop has to restart.
I also tried "on error got 0, on error got -1" but it did not work.
Below is my Code - Kindly pardon me for poor coding I am new to VBA.
Sub Get_Data()
Sheets("Sheet2").Select
Range("E2").Select
Range("H6:H120").ClearContents
Dim IE As Object
Set IE = CreateObject("InternetExplorer.application")
IE.Visible = True
Dim E As Long
Dim S As Long
E = Range("A" & Rows.Count).End(xlUp).Row
JumpToHere:
For j = S To E
S = Range("H" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").Select
Range("E" & S).Select
ActiveCell.Offset(1, -2).Select
Dim X As Variant
X = ActiveCell.Value
IE.navigate X
Do
If IE.ReadyState = 4 Then
IE.Visible = True
Exit Do
Else
DoEvents
End If
Loop
ActiveCell.Offset(0, 1).Select
Dim Y As Variant
Y = ActiveCell.Value
IE.document.all("username").Value = Y
ActiveCell.Offset(0, 1).Select
Dim Z As Variant
Z = ActiveCell.Value
IE.document.all("password").Value = Z
IE.document.all("merchant_login_submit_button").Click
Application.Wait (Now + TimeValue("0:00:8"))
Set ElementCol = IE.document.getElementsByTagName("span")
For Each link In ElementCol
If link.innerHTML = "Authentication Failed" Then
ActiveCell.Offset(0, 3).Value = "Authentication Failed"
GoTo JumpToHere
End If
Next
Set tags = IE.document.getElementsByTagName("input")
For Each tagx In tags
If tagx.Value = "Continue to Control Panel" Then
tagx.Click
Application.Wait (Now + TimeValue("0:00:3"))
Exit For
End If
Next
Set ElementCol = IE.document.getElementsByTagName("a")
For Each link In ElementCol
If link.innerHTML = "Reports" Then
link.Click
End If
Next
Application.Wait (Now + TimeValue("0:00:06"))
Dim checkdate As Integer
checkdate = Format(Date, "dd") - 1
IE.document.getElementById("snapshot_group_by").Value = "payment_processor"
IE.document.getElementById("snapshot_end_date_day").Value = checkdate
IE.document.all("reports_submit_button").Click
Application.Wait (Now + TimeValue("0:00:3"))
Dim ws As Worksheet
Dim rng As Range
Dim tbl As Object
Dim rw As Object
Dim cl As Object
Dim tabno As Long
Dim nextrow As Long
Dim I As Long
Set ws = Worksheets.Add
For Each tbl In IE.document.getElementsByTagName("TABLE")
tabno = tabno + 1
nextrow = nextrow + 1
Set rng = ws.Range("B" & nextrow)
rng.Offset(, -1) = "Table " & tabno
For Each rw In tbl.Rows
For Each cl In rw.Cells
rng.Value = cl.outerText
Set rng = rng.Offset(, 1)
I = I + 1
Next cl
nextrow = 0
Set rng = rng.Offset(1, -I)
I = 0
Next rw
Next tbl
ws.Cells.ClearFormats
Sheets("Sheet2").Select
ActiveCell.Offset(0, 3).Value = ActiveSheet.Previous.Range("F4")
Application.DisplayAlerts = False
ActiveSheet.Previous.Delete
Application.DisplayAlerts = True
Set ElementCol = IE.document.getElementsByTagName("a")
For Each link In ElementCol
If link.innerHTML = "Logout" Then
link.Click
End If
Next
Next j
End Sub
Sounds like your real problem is that your code isn't properly waiting. Instead of Application.Wait, use a proper waiting loop any time you invoke the IE.Navigate or any element .Click or form .Submit event.
VBA HTML not running on all computers
Otherwise, you don't have any active error-trapping in your code. Wrap your loop with On Error statements, as below.
The first one, On Error GoTo MyErrorHandler instructs the program of what to do if an error is encountered within the loop. If there's an error, the code underneath the MyErrorHandler label will execute, and resume at the NextJ label. Once the loop finishes, On Error GoTo 0 returns normal (i.e., none) error-handling. Any errors occurring outside the loop still raise an exception during runtime.
Option Explicit
Sub Get_Data()
'// Dim your variables
'// Executable code starts here
JumpToHere:
For j = S To E
On Error GoTo MyErrorHandler
' Now ANY ERROR, ANYWHERE in the loop will go to the error handler
NextJ:
Next j
'// Code below this line won't be subject to the error handler
On Error GoTo 0
'// more code if you have it
' Exit gracefully if there was no error:
Exit Sub
'// Here is the error handler:
MyErrorHandler:
Err.Clear()
Resume NextJ
End Sub
If you truly want to re-start the loop, then instead of NextJ, do Resume JumpToHere.
Related
Right now I have code which opens e-mail if ID given in user-form(TextBox1INC) is found in Column1, but let's say I have two e-mails or whatever the number is and I want to open all of them and not only one. How Do I put loop inside this code to make this work ?
Private Sub CommandButton8showemail_Click()
Dim wsArch As Worksheet
Dim lastrow, a As Long
Dim strEmail, strEmailLoc As String
Dim OutMejlik As Outlook.Application
Dim msg As Outlook.MailItem
Set wsArch = ThisWorkbook.Sheets("Emails_arch")
lastrow = Sheets("Emails_arch").Range("A" & Rows.Count).End(xlUp).Row
With wsArch
For a = lastrow To 2 Step -1
If .Cells(a, 1).Value = TextBox1INC.Text Then
strEmailLoc = .Cells(a, 2).Value
Set OutMejlik = CreateObject("Outlook.Application")
Set msg = OutMejlik.Session.OpenSharedItem(strEmailLoc)
msg.Display
Exit Sub
End If
Next a
End With
End Sub
Currently in the loop you are exiting as soon as the first item is displayed to a user:
For a = lastrow To 2 Step -1
If .Cells(a, 1).Value = TextBox1INC.Text Then
strEmailLoc = .Cells(a, 2).Value
Set OutMejlik = CreateObject("Outlook.Application")
Set msg = OutMejlik.Session.OpenSharedItem(strEmailLoc)
msg.Display
Exit Sub
End If
Next a
End With
If you remove the Exit Sub part the code will continue running and opening items as you need. But also I'd recommend creating a new Outlook Application outside of the loop to avoid creation each time (even if Outlook is a singleton and only one instance can be created).
Set OutMejlik = CreateObject("Outlook.Application")
With wsArch
For a = lastrow To 2 Step -1
If .Cells(a, 1).Value = TextBox1INC.Text Then
strEmailLoc = .Cells(a, 2).Value
Dim msg As Outlook.MailItem
Set msg = OutMejlik.Session.OpenSharedItem(strEmailLoc)
msg.Display
End If
Next a
End With
I wrote excel macro to fetch data from multiple pages ( here around 25-40 pages ) . I have managed to change pages and scrape all pages from every page .
Sub Fetch_Data()
Dim IE As Object
Dim httpReq As Object
Dim HTMLdoc As Object
Dim resultsTable As Object
Dim tRow As Object, tCell As Object
Dim destCell As Range
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = False
'Application.ScreenUpdating = False
Application.StatusBar = "Data Fetching in progress, please wait..."
IE.Navigate "https://www.bseindia.com/markets/debt/TradenSettlement.aspx" 'load the Backshop Loan Locator page
Do
DoEvents
Loop Until IE.ReadyState = 4
Set HTMLdoc = IE.Document
'LR = Cells(Rows.Count, 1).End(xlUp).Row
With ActiveSheet
'.Cells.ClearContents
Set destCell = .Range("A1")
End With
Set resultsTable = HTMLdoc.getElementById("ContentPlaceHolder1_GridViewrcdsFC")
For Each tRow In resultsTable.Rows
For Each tCell In tRow.Cells
destCell.Offset(tRow.RowIndex, tCell.cellIndex).Value = tCell.innerText
Next
Next
'________________________________________________________________________________________________________________________
'Go to Next page
'IE.Navigate "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC','Page$2')"
i = 2
For i = 2 To 50
If i = 2 Then
Url = "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC','Page$2')"
On Error GoTo ErrorHandler
ElseIf i = 3 Then
Url = "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC','Page$3')"
On Error GoTo ErrorHandler
ElseIf i = 4 Then
Url = "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC','Page$4')"
On Error GoTo ErrorHandler
ElseIf i = 5 Then
Url = "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC','Page$5')"
On Error GoTo ErrorHandler
ErrorHandler:
GoTo XYZ
End If
IE.Navigate Url
Do
DoEvents
Loop Until IE.ReadyState = 4
Url = ""
LR = Cells(Rows.Count, 1).End(xlUp).Row - 1
With ActiveSheet
'.Cells.ClearContents
Set destCell = .Range("A" & LR)
End With
Set resultsTable = HTMLdoc.getElementById("ContentPlaceHolder1_GridViewrcdsFC")
For Each tRow In resultsTable.Rows
For Each tCell In tRow.Cells
destCell.Offset(tRow.RowIndex, tCell.cellIndex).Value = tCell.innerText
Next
Next
Next i
'________________________________________________________________________________________________________________________
XYZ: IE.Quit
Application.StatusBar = "Data Fetching Completed"
MsgBox ("Data Successfully Fetched")
Application.StatusBar = ""
Dim lrow As Long
Dim index As Long
Dim header As String
header = Range("A1").Value
lrow = Range("A" & Rows.Count).End(xlUp).Row
For index = 2 To lrow
If Range("A" & index).Value = header Then Rows(index).Delete
Next
End Sub
I want to change pages automatically without writing every page , I tried something like below , but pages are not getting changed , how to loop through pages :
For i = 2 To 4
x = "Page$" + CStr(i)
Url = "javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridViewrcdsFC'," & x & ")"
On Error GoTo ErrorHandler
ErrorHandler:
GoTo XYZ
You have to look if there are url links to the other pages on the current page, find a tag and loop all the webpages. You can also look voor the url of each page and hardcode it.
Example with urls beneath tag "a":
Set AElements = HTMLDoc.getElementsByTagName("a")
For Each AElement In AElements
If AElement.id = "xxxxxxxxx" Then
Cells(Cell, 27) = AElement.src 'I write URL in the 27th column
'AElement.href
End If
Next AElement
I'm trying to access webpage elements that I may need to scrape.
I have a handle on how to access headers by tag name but I am having trouble with the div.address tag in the image.
The Inspect Element view
My code. Problem line is marked with +++++++:
Sub gettitleheader()
Dim wb As Object
Dim doc As Object
Dim sURL As String
Dim lastrow As Long
lastrow = Sheet1.Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
Set wb = CreateObject("internetExplorer.Application")
sURL = Cells(i, 1)
wb.navigate sURL
wb.Visible = True
While wb.Busy
DoEvents
Wend
'HTML document
Set doc = wb.document
On Error GoTo err_clear
Cells(i, 2) = doc.getelementsbytagname("h1")(0).innerText
err_clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
wb.Quit
On Error GoTo err2_clear
Cells(i, 3) = doc.getelementsbytagname("address")(0).innerHTML '+++++++
err2_clear:
If Err2 <> 0 Then
Err2.Clear
Resume Next
End If
wb.Quit
Range(Cells(i, 1), Cells(i, 4)).Columns.AutoFit
Next i
End Sub
In the image below, the h1 tag is scraped and populated but not the address text.
Final output image, (I'm trying to fill that empty box)
Answered my own question! so proud!
Sub gettitleheader()
Dim wb As Object
Dim doc As Object
Dim y As String
Dim z As String
Dim sURL As String
Dim lastrow As Long
lastrow = Sheet1.Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
Set wb = CreateObject("internetExplorer.Application")
sURL = Cells(i, 1)
wb.navigate sURL
wb.Visible = True
While wb.Busy
DoEvents
Wend
'HTML document
Set doc = wb.document
On Error GoTo err_clear
Cells(i, 2) = doc.getElementsByTagName("h1")(0).innerText
y = doc.getElementsByClassName("address")(0).innerText
Do While (InStr(y, Chr(10)))
y = Right(y, Len(y) - 1)
Loop
z = y
Cells(i, 3) = z
err_clear:
If Err <> 0 Then
Err.Clear
Resume Next
End If
wb.Quit
I get this error:
subscript out of range.
I do not have the debug option, only OK and HELP.
One time on 20 the macro works. The rest of time I'm getting this error.
The code makes you choose the path you want to search and next the text you want to find in the workbook in the path chosen. It searches in sub folders too. After that it sends back the file name, sheet name, which cell and what text is in the cell.
The macro runs but pops that error after searching 4 to 5 different files.
Dim AppObject As New Class1
Public WS As Worksheet
Sub SearchWKBooksSubFolders(Optional Folderpath As Variant, Optional Str As Variant)
Dim myfolder As String
Dim a As Single
Dim sht As Worksheet
Dim Lrow As Single
Dim Folders() As String
Dim Folder As Variant
ReDim Folders(0)
Dim wbk As Workbook
If IsMissing(Folderpath) Then
Set WS = Sheets.Add
With Application.FileDialog(msoFileDialogFolderPicker)
.Show
myfolder = .SelectedItems(1) & "\"
End With
Str = Application.InputBox(prompt:="Nom de la Personne:", Title:="Personne a chercher", Type:=2)
If Str = "" Then Exit Sub
WS.Range("A1") = "Semaine"
WS.Range("B1") = "Journée"
WS.Range("C1") = "Cellule"
WS.Range("D1") = "Nom"
Folderpath = myfolder
Value = Dir(myfolder, &H1F)
Else
If Right(Folderpath, 2) = "\\" Then
Exit Sub
End If
Value = Dir(Folderpath, &H1F)
End If
Do Until Value = ""
If Value = "." Or Value = ".." Then
Else
If GetAttr(Folderpath & Value) = 16 Then
Folders(UBound(Folders)) = Value
ReDim Preserve Folders(UBound(Folders) + 1)
ElseIf Right(Value, 3) = "xls" Or Right(Value, 4) = "xlsx" Or Right(Value, 4) = "xlsm" Then
On Error Resume Next
Workbooks.Open Filename:=Folderpath & Value, Password:="zzzzzzzzzzzz"
If Err.Number <> 0 Then
WS.Range("A4").Offset(a, 0).Value = Value
WS.Range("B4").Offset(a, 0).Value = "Password protected"
a = a + 1
On Error GoTo 0
Else
For Each sht In ActiveWorkbook.Worksheets
'Expand all groups in sheet
sht.Outline.ShowLevels RowLevels:=8, ColumnLevels:=8
Set c = sht.Cells.Find(Str)
If Not c Is Nothing Then
firstAddress = c.Address
Do
Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row
WS.Range("A1").Offset(Lrow, 0).Value = Value
WS.Range("B1").Offset(Lrow, 0).Value = sht.Name
WS.Range("C1").Offset(Lrow, 0).Value = c.Address
WS.Range("D1").Offset(Lrow, 0).Value = c.Value
Set c = sht.Cells.FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
Next sht
End If
Workbooks(Value).Close False
On Error GoTo 0
End If
End If
Value = Dir
Loop
For Each Folder In Folders
SearchWKBooksSubFolders (Folderpath & Folder & "\")
Next Folder
Cells.EntireColumn.AutoFit
End Sub
you can debug on your own without the debugger of the IDE.
Simply put On Error Resume Next/On Error Goto 0 very close to each other in order to restrict the statements which can raise errors. ie the second On Error Goto 0 is too far.
I can debug more effectively simply putting between statements something like that:
a = a + 1
debug.print "I am here"
b = b -5
debug.print "I am there"
c = 5 / 0
debug.print "You can't see me"
So you can find when the error is raised
I have a macro which takes the body of an email, splits it into an array and places it into excel. It then uses colons to split rows into [label] and [data].
For some reason it has stopped working. I had some good help here but it has now failed in the second subroutine and I can't get my head around the error. I am sure it's something simple, possibly related to running from outlook or incorrect definition of ranges. Everything using rows, cells, range object etc gives this error.
The exact error is Runtime 1004 error. Method [cells, rows] of object Global failed
I have used a comment to mark the point where problems begin:
Private oXLApp As Object, oXLWb As Object, oXLWs As Object
Sub Thermo_to_excel()
Dim myOlApp As Object, mynamespace As Object
Dim ThermoMail As Object
Dim msgText, delimtedMessage, Delim1 As String
Set myOlApp = Outlook.Application
Set mynamespace = myOlApp.GetNamespace("mapi")
Set ThermoMail = Application.ActiveInspector.CurrentItem
delimtedMessage = ThermoMail.Body
'~~> Establish an EXCEL application object
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
'~~> If not found then create new instance
If Err.Number <> 0 Then
Set oXLApp = CreateObject("Excel.Application")
End If
Err.Clear
On Error GoTo 0
Set oXLWb = oXLApp.Workbooks.Add
Set oXLWs = oXLWb.Sheets("Sheet1")
'Truncated [Array definition goes here]
With oXLWs
.Range(.Cells(1, 1), .Cells(lastRow, 1)).Value = _
oXLApp.WorksheetFunction.Transpose(messageArray)
End With
Call splitAtColons
ThermoMail.Close (olDiscard)
End Sub
Sub splitAtColons()
Dim Roows As Integer
'PROBLEMS start here now
Roows = Cells(oXLWs.Rows.Count & "," & oXLWs.ActiveCell.Column).End(xlUp).Row
Range("A1").Select
Range("A1:B" & Roows).Font.Name = Range("B1").Font.Name
Range("A1:B" & Roows).NumberFormat = "#"
'Application.ScreenUpdating = False
Do Until Z = Roows
If Not InStr(ActiveCell.Value, ":") = 0 Then
Cells(ActiveCell.Row, 2).Value = Trim(Mid(ActiveCell.Value, InStr(ActiveCell.Value, ":") + 1))
ActiveCell.Value = Left(ActiveCell.Value, InStr(ActiveCell.Value, ":"))
Else
Cells(ActiveCell.Row, 2).Value = Trim(ActiveCell.Value)
ActiveCell.Value = ""
End If
If ActiveCell.Value = "" And Range("B" & ActiveCell.Row).Value = "" Then
ActiveCell.EntireRow.Delete
Roows = Roows - 1
Z = Z - 1
End If
Range("A" & Z + 2).Select
Z = Z + 1
Loop
Columns("B:B").EntireColumn.AutoFit
Columns("A:A").EntireColumn.AutoFit
'Application.ScreenUpdating = True
End Sub
I believe your problem is that you are not fully defining your Range, Cells, & Columns functions. Try adding oXLWs. before each of those functions, or add a With oXLWs line and add . before each of those functions and see what happens.
If you run this in Excel, the Range, Cells, & Columns functions will work because they will refer to the active worksheet. If you run this through Outlook, it may not work if the sheet is not active.