How to scrape web data in vba - vba

I have followed jsotola's suggestion and recorded the following macro, but encountered an error, how can I solve it?
Run time error 91 and the following code has been highlighted
Selection.ListObject.TableObject.Refresh
Sub Macro1()
ActiveWorkbook.Queries.Add Name:="1-1-1", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Web.Page(Web.Contents(""http://www.hkjc.com/English/racing/Horse.asp?HorseNo=V099""))," & Chr(13) & "" & Chr(10) & " Data0 = Source{0}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Data0,{{""Column1"", type text}, {""Column2"", type text}, {""Column3"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
ActiveWorkbook.Queries.Add Name:="1-1-2", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Web.Page(Web.Contents(""http://www.hkjc.com/English/racing/Horse.asp?HorseNo=V099""))," & Chr(13) & "" & Chr(10) & " Data1 = Source{1}[Data]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Data1,{{""Column1"", type text}, {""Column2"", type text}, {""Column3"", type text}, {""Column4"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Changed Type"""
Workbooks("Book1").Connections.Add2 "Query - Table 0", _
"Connection to the 'Table 0' query in the workbook.", _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=Table 0" _
, """Table 0""", 6, True, False
Workbooks("Book1").Connections.Add2 "Query - Table 1", _
"Connection to the 'Table 1' query in the workbook.", _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=Table 1" _
, """Table 1""", 6, True, False
Sheets.Add After:=ActiveSheet
Selection.ListObject.TableObject.Refresh
Sheets.Add After:=ActiveSheet
Selection.ListObject.TableObject.Refresh
End Sub

You could use the following script.
① I grab the left hand side links with
.getElementsByTagName("table")(3).getElementsByTagName("a")
As these return relative paths starting with "about:", I replace this part with the fixed prefix string BASESTRING. This gives the absolute path.
② I target the table with main info by getting get a collection of the table tags and selecting the appropriate table by index.
Set hTable = .getElementsByTagName("table")(6)
③ Additionally, as targeting by className is not supported with method I am using, due to late bound HTMLfile I assume), I parse the sub header out "SMART BOY (V076)" from the innerHTML of an element containing this info. Otherwise, it could have been targeted more cleanly with .getElementsByClassName("subsubheader")(0)
Example data on page:
Example output from code:
Code:
Option Explicit
Public Sub GetTable()
Dim sResponse As String, hTable As Object
Application.ScreenUpdating = False
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "http://www.hkjc.com/english/racing/horse.asp?HorseNo=V076", False
.send
sResponse = StrConv(.responseBody, vbUnicode)
End With
sResponse = Mid$(sResponse, InStr(1, sResponse, "HEAD"))
With CreateObject("htmlFile")
.Write sResponse
Set hTable = .getElementsByTagName("table")(6)
Dim links As Object, title As String
Set links = .getElementsByTagName("table")(3).getElementsByTagName("a")
title = Replace$(Split(Split(.getElementsByTagName("table")(2).innerHTML, "title_eng_text>")(1), "<")(0), " ", vbNullString)
End With
Dim tSection As Object, tRow As Object, tCell As Object, tr As Object, td As Object, r As Long, c As Long, hBody As Object
Set hBody = hTable.getElementsByTagName("tbody")
Const BASESTRING As String = "http://www.hkjc.com/english/racing/"
With ActiveSheet
.Cells(1, 1) = title
r = 2
For Each tSection In hBody 'HTMLTableSection
Set tRow = tSection.getElementsByTagName("tr") 'HTMLTableRow
For Each tr In tRow
Set tCell = tr.getElementsByTagName("td")
c = 1
.Cells(r, c) = links(r - 1).innerHTML
.Cells(r, c + 1) = Replace$(links(r - 1), "about:", BASESTRING)
For Each td In tCell 'DispHTMLElementCollection
.Cells(r, c + 2).Value = td.innerText 'HTMLTableCell
c = c + 1
Next td
r = r + 1
Next tr
Next tSection
.UsedRange.Columns.AutoFit
End With
Application.ScreenUpdating = True
End Sub

Related

If Then to un-protect and protect document

I have a userform that unprotects a document to let info be entered then protects the document. All of my subs work aside one.
Ranges with if/thens don't work but basic if then works.
Example of sub that works:
Private Sub ComboBox5_Change()
ActiveDocument.Unprotect "password"
Dim ComboBox5 As Range
Set ComboBox5 = ActiveDocument.Bookmarks("bmragpd").Range
ComboBox5.Text = Me.ComboBox5.Value
If Me.ComboBox5.Value = "No" Then
ComboBox5.Text = "205.55a"
End If
If Me.ComboBox5.Value = "Yes" Then
ComboBox5.Text = ""
End If
ActiveDocument.Protect wdAllowOnlyFormFields, NoReset:=True, Password:="password"
End Sub
This sub will say the document is already unprotected.
I tried removing the unprotect on combobox6:
Private Sub ComboBox6_Change()
ActiveDocument.Unprotect "password"
Dim rngComboBox6 As Range
Dim sssaText As String
Dim iiia As Integer
Set rngComboBox6 = ActiveDocument.Bookmarks("bmfcs").Range
sssaText = ComboBox6.Value
If Me.ComboBox6.Value = "Yes" Then
For iiia = 1 To 1
sssaText = sssaText & Chr(13) & "200" _
& Chr(13) & "200.1" _
& Chr(13) & "" _
& Chr(13) & "OEBS" _
& Chr(13) & "" _
& Chr(13) & "21c" _
& Chr(13) & "" _
& Chr(13) & "22c" _
& Chr(13) & "Yes" _
& Chr(13) & "" _
& Chr(13) & "Yes" _
& Chr(13) & "Two" _
& Chr(13) & "" _
& Chr(13) & "ES2a.1" _
& Chr(13) & "" _
& Chr(13) & "222" _
& Chr(13) & "" _
& Chr(13) & "222a" _
& Chr(13) & "222b" _
& Chr(13) & "" _
& Chr(13) & "3.a.1" _
& Chr(13) & "" _
& Chr(13) & "NA" _
& Chr(13) & "" _
& Chr(13) & "I. TuuVa"
Next iiia
sssaText = sssaText & Chr(13) & "717217" _
& Chr(13) & "" _
& Chr(13) & "1212" _
& Chr(13) & "" _
& Chr(13) & "D.1" _
& Chr(13) & "F2B-4"
End If
rngComboBox6.Text = sssaText
ActiveDocument.Bookmarks.Add "bmfcs", rngComboBox6
If Me.ComboBox6.Value = "No" Then
ComboBox6.Text = ""
End If
ActiveDocument.Protect wdAllowOnlyFormFields, NoReset:=True, Password:="password"
End Sub
The first End If is in the wrong place. You are adding text to the document regardless of the value of the combo box.
It is not good practice to use a control’s change event to commit changes to a document. Apart from anything else it doesn’t allow the user to cancel without making changes.
Instead use an OK/Apply/Finish button.
Then you only need to unprotect/reprotect the document once.

how do i move this sub from sheet 1 to sheet 2

I need to move this search to a userform that currently performs a search on sheet 1 I would like to put the database on sheet 2, everything works but the insert function cannot find the last row.
Private Sub ComboBox1_Change()
Dim Riga As Long
If ComboBox1.Value <> "" Then
Riga = ComboBox1.ListIndex + 2
Cells(Riga, 1).Select
TextBox1 = ComboBox1.Value
TextBox2 = Sheets(2).Cells(ActiveCell.Row, 2)
TextBox3 = Sheets(2).Cells(ActiveCell.Row, 3)
TextBox4 = Sheets(2).Cells(ActiveCell.Row, 4)
TextBox5 = Sheets(2).Cells(ActiveCell.Row, 5)
TextBox6 = Sheets(2).Cells(ActiveCell.Row, 6)
End If
End Sub
Private Sub CommandButton_Chiudi_Click()
Unload Me
End Sub
Private Sub CommandButton_Nuovo_Click()
Dim tx As Byte
ComboBox1.Value = ""
Cells(Range("A1").End(xlDown).Row + 1, 1).Select
For tx = 1 To 6
Userform1.Controls("TextBox" & tx).Text = ""
Next tx
TextBox1.SetFocus
End Sub
Private Sub CommandButton_Cancella_Click()
Dim Messaggio As String, Stile As String, Titolo As String
Dim Risposta As VbMsgBoxResult
Titolo = "Cancellazione Record."
Messaggio = "Vuoi cancellare il tesserino:" & Chr(10) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 1) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 2) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 3) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 4) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 5) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 6) & Chr(10)
Stile = vbYesNo + vbQuestion + vbDefaultButton2
Risposta = MsgBox(Messaggio, Stile, Titolo)
If Risposta = vbNo Then End
ActiveCell.EntireRow.Delete
End
End Sub
Private Sub CommandButton_Aggiorna_Click()
Dim Riga As Long
Dim Messaggio As String, Stile As String, Titolo As String
Dim Risposta As VbMsgBoxResult
Titolo = "Cancellazione Record."
Messaggio = "Vuoi registrare il tesserino:" & Chr(10) & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 1).Value & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 2).Value & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 3).Value & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 4).Value & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 5).Value & Chr(10) _
& Sheets(2).Cells(ActiveCell.Row, 6).Value & Chr(10) & Chr(10) _
& "con:" & Chr(10) _
& TextBox1.Value & Chr(10) _
& TextBox2.Value & Chr(10) _
& TextBox3.Value & Chr(10) _
& TextBox4.Value & Chr(10) _
& TextBox5.Value & Chr(10) _
& TextBox6.Value
Stile = vbYesNo + vbQuestion + vbDefaultButton2
Risposta = MsgBox(Messaggio, Stile, Titolo)
If Risposta = vbNo Then End
Riga = ActiveCell.Row
ComboBox1.Value = ""
Sheets(2).Cells(Riga, 1) = TextBox1.Value
Sheets(2).Cells(Riga, 2) = TextBox2.Value
Sheets(2).Cells(Riga, 3) = TextBox3.Value
Sheets(2).Cells(Riga, 4) = TextBox4.Value
Sheets(2).Cells(Riga, 5) = TextBox5.Value
Sheets(2).Cells(Riga, 6) = TextBox6.Value
ComboBox1.SetFocus
End Sub
how can I do to indicate that with the insert new record function I find the last row on the database that I have moved to sheet 2? thank you
sheets(2).Cells(Range("A1").End(xlDown).Row + 1, 1).Select
it doesn't work and I go into error
There are two easy methods. Try them out use what ever works fine for your case.
Method 1:
Sheets(2).UsedRange.Rows.Count
Method 2:
Sheets(2).Cells(Rows.Count, col_no).End(xlUp).Row
Here you need to change col_no to your desired column. For the above example it would be like
Method 2:
Sheets(2).Cells(Rows.Count, 1).End(xlUp).Row
These both method will give you last data containing row you need to add one ( +1 ) to get next blank row for new data entry.

VBA - Loop, Catch error, assign variable and continue looping?

I have this email automation program. I essentially want to create a error catch for RecpName. When RecpName is passed into Lotus Notes and returns an error (due to spelling errors), I want to capture that into a error catch.
I still want the loop to keep going and continue down the list, but tell the user which names it couldn't send emails to.
Here's my code:
Sub Send_HTML_Email()
Const ENC_IDENTITY_8BIT = 1729
'Send Lotus Notes email containing links to files on local computer
Dim NSession As Object 'NotesSession
Dim NDatabase As Object 'NotesDatabase
Dim NStream As Object 'NotesStream
Dim NDoc As Object 'NotesDocument
Dim NMIMEBody As Object 'NotesMIMEEntity
Dim SendTo As String
Dim subject As String
Dim HTML As String, HTMLbody As String
Dim wb As Workbook
Dim ws As Worksheet
Dim lstrow As Long, j As Long
Dim RecpName As String, candiName As String
Dim a As Hyperlink
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Detail")
' Instantiate the Lotus Notes COM's Objects.
lstrow = ws.Range("B" & Rows.Count).End(xlUp).Row
Set NSession = CreateObject("Notes.NotesSession") 'using Lotus Notes Automation Classes (OLE)
Set NDatabase = NSession.GetDatabase("", "")
If Not NDatabase.IsOpen Then NDatabase.OPENMAIL
For j = 3 To lstrow
RecpName = ws.Cells(j, 2).Text
candiName = ws.Cells(j, 1).Text
SendTo = RecpName
subject = wb.Worksheets("Email Settings").Range("B1").Text
Debug.Print subject
Set NStream = NSession.CreateStream
HTMLbody = "<p>" & "Hi " & ws.Cells(j, 2).Text & "," & "</p>" & _
vbCrLf & _
"<p>" & Sheets("Email Settings").Cells(2, 2).Text & vbCrLf & _
Sheets("Detail").Cells(j, 1).Text & "</p>" & vbCrLf & _
"<p>" & Sheets("Email Settings").Cells(3, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(4, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(5, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(6, 2).Text & "</p>" & _
"<p>" & Sheets("Email Settings").Cells(9, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(10, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(11, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(12, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(13, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(14, 2).Text & _
"<br>" & Sheets("Email Settings").Cells(15, 2).Text & "</p>"
HTML = "<html>" & vbLf & _
"<head>" & vbLf & _
"<meta http-equiv=""Content-Type"" content=""text/html; charset=UTF-8""/>" & vbLf & _
"</head>" & vbLf & _
"<body>" & vbLf & _
HTMLbody & _
"</body>" & vbLf & _
"</html>"
NSession.ConvertMime = False 'Don't convert MIME to rich text
Set NDoc = NDatabase.CreateDocument()
With NDoc
.Form = "Memo"
.subject = subject
.SendTo = Split(SendTo, ",")
Set NMIMEBody = .CreateMIMEEntity
NStream.WriteText HTML
NMIMEBody.SetContentFromText NStream, "text/html; charset=UTF-8", ENC_IDENTITY_8BIT
.Send False
.Save True, False, False
End With
NSession.ConvertMime = True 'Restore conversion
Next j
Set NDoc = Nothing
Set NSession = Nothing
MsgBox "The e-mail has successfully been created and distributed", vbInformation
End Sub
Maybe this code can help you:
Sub Send_HTML_Email()
Dim cnt_err As Integer: cnt_err = 1
On Error GoTo ErrorHandler
Const ENC_IDENTITY_8BIT = 1729
' Insert the rest of the code here
MsgBox "The e-mail has successfully been created and distributed", vbInformation
Exit Sub
ErrorHandler:
' Insert code to handle the error, e.g.
wb.Worksheets("SheetToSaveMailsNotSent").Range("A" & cnt) = RecpName
cnt = cnt + 1
' The next instruction will continue the subroutine execution
Resume Next
End Sub
For more help you can go to this link.
HTH ;)

Excel VBA Comment Query

So I have tried and failed many times to do this.
I'm trying to write a VBA script that when run will insert a comment in the active cell that says:
[Surname], [First Name]
[Todays date] was [current cell value]
Surnames and First Names should be bold.
If the cell is edited again it would edit the comment and put the above format below the previous comment.
Here is what I have at the moment (bearing in mind it could be very wrong)
Sub Test()
Dim UserN As String
Dim commt As String
Dim cmt As Comment
UserN = Application.UserName
Set cmt = ActiveCell.Comment
If ActiveCell.Comment Is Nothing Then
commt = UserN & Chr(10) & Chr(10) _
& Chr(10) & Format(Now, strDate) & " was £" & ActiveCell
With Selection
With Cells(Selection.Row, Selection.Column)
.ClearComments
.AddComment
.Comment.Visible = False
.Comment.Shape.AutoShapeType = msoShapeRoundedRectangle
.Comment.Text Text:=commt
With .Comment.Shape.TextFrame
' Username
With .Characters(1, Len(UserN)).Font
.Bold = True
End With
With .Characters.Font
.Size = 12
End With
End With
.Comment.Shape.TextFrame.AutoSize = True
End With
End With
Else
commt.txt = commt.txt & Chr(10) UserN & Chr(10) & Chr(10)
& Chr(10) & Format(Now, strDate) & " was £" & ActiveCell
With Selection
With Cells(Selection.Row, Selection.Column)
.ClearComments
.AddComment
.Comment.Visible = False
.Comment.Shape.AutoShapeType = msoShapeRoundedRectangle
.Comment.Text Text:=commt
With .Comment.Shape.TextFrame
' Username
With .Characters(1, Len(UserN)).Font
.Bold = True
End With
With .Characters.Font
.Size = 12
End With
End With
.Comment.Shape.TextFrame.AutoSize = True
End With
End With
End If
End Sub
I think the only major issue is with this line:
commt.txt = commt.txt & Chr(10) UserN & Chr(10) & Chr(10)
& Chr(10) & Format(Now, strDate) & " was £" & ActiveCell
It is using the variable 'commt' as if it were a comment object, but is simply a string. It's also missing a '&' and a '_' line separator. I believe the line should be:
commt = cmt.text & Chr(10) & UserN & Chr(10) & Chr(10) & Chr(10) & Format(Now, strDate) & " was £" & ActiveCell

Output doesn't match input

I've created a macro that's meant to created a lump of CSS & HTML from a set of values in each sheet of a spreadsheet.
It's a little untidy as I created the function to write it from one sheet first as a proof of concept, and then updated it.
It doesn't throw any obvious errors, but the output varies, sometimes it shows the same thing both times, and then depending on where I've got debug MsgBoxs or watches in VBA seems to alter the output.
Any ideas what on earth i'm doing wrong?
Sub createCode()
Dim myWorkbook As Workbook
Dim mySheet As Worksheet
Set myWorkbook = Application.ActiveWorkbook
For Each mySheet In myWorkbook.Worksheets
Dim bannerCount As Integer
Dim BannerCollection() As Banner
Dim r As Range
Dim lastRow, lastCol
Dim allCells As Range
bannerCount = 0
lastCol = mySheet.Range("a2").End(xlToRight).Column
lastRow = mySheet.Range("a2").End(xlDown).Row
Set allCells = mySheet.Range("a2", mySheet.Cells(lastRow, lastCol))
' MsgBox (mySheet.Name)
' MsgBox ("lastRow:" & lastRow & "lastCol:" & lastCol)
ReDim BannerCollection(allCells.Rows.Count)
For Each r In allCells.Rows
Dim thisBanner As Banner
thisBanner.imagePath = ""
thisBanner.retImagePath = ""
thisBanner.bannerTitle = ""
thisBanner.urlPath = ""
bannerCount = bannerCount + 1
' MsgBox (bannerCount)
thisBanner.imagePath = Cells(r.Row, 2).Value
thisBanner.retImagePath = Cells(r.Row, 3).Value
thisBanner.bannerTitle = Cells(r.Row, 4).Value
thisBanner.urlPath = Cells(r.Row, 5).Value
'MsgBox (Cells(r.Row, 2).Value)
'MsgBox (Cells(r.Row, 3).Value)
'MsgBox (Cells(r.Row, 4).Value)
'MsgBox (Cells(r.Row, 5).Value)
BannerCollection(bannerCount - 1) = thisBanner
Next r
Dim i As Variant
Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String
retinaCSS = ""
imgCSS = ""
firstBannerCode = ""
otherBannersCode = ""
bannerTracking = ""
For i = 0 To bannerCount - 1
bannerTracking = BannerCollection(i).bannerTitle
bannerTracking = Replace(bannerTracking, " ", "+")
bannerTracking = Replace(bannerTracking, "&", "And")
bannerTracking = Replace(bannerTracking, "%", "PC")
bannerTracking = Replace(bannerTracking, "!", "")
bannerTracking = Replace(bannerTracking, "£", "")
bannerTracking = Replace(bannerTracking, ",", "")
bannerTracking = Replace(bannerTracking, "'", "")
bannerTracking = Replace(bannerTracking, "#", "")
bannerTracking = Replace(bannerTracking, ".", "")
retinaCSS = retinaCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).retImagePath & "');}" & vbNewLine
imgCSS = imgCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).imagePath & "');}" & vbNewLine
If i = 0 Then
firstBannerCode = firstBannerCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
firstBannerCode = firstBannerCode & "" & vbNewLine
firstBannerCode = firstBannerCode & "</div>" & vbNewLine
Else
otherBannersCode = otherBannersCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
otherBannersCode = otherBannersCode & "" & vbNewLine
otherBannersCode = otherBannersCode & "</div>" & vbNewLine
End If
' MsgBox (BannerCollection(i).retImagePath & vbNewLine & BannerCollection(i).imagePath & vbNewLine & BannerCollection(i).bannerTitle & vbNewLine & BannerCollection(i).urlPath)
Next i
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & "/* Banners */" & vbNewLine
CodeString = CodeString & imgCSS
CodeString = CodeString & "/* Retina Banners */" & vbNewLine
CodeString = CodeString & "#media only screen and (-webkit-min-device-pixel-ratio: 2) {" & vbNewLine
CodeString = CodeString & retinaCSS
CodeString = CodeString & "}" & vbNewLine
CodeString = CodeString & "</style>" & vbNewLine
CodeString = CodeString & "<div id=" & Chr(34) & "sliderTarget" & Chr(34) & " class=" & Chr(34) & "slides" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & firstBannerCode
CodeString = CodeString & "</div>" & vbNewLine
CodeString = CodeString & "<script id=" & Chr(34) & "sliderTemplate" & Chr(34) & " type=" & Chr(34) & "text/template" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & otherBannersCode
CodeString = CodeString & "</script>"
FilePath = Application.DefaultFilePath & "\" & mySheet.Name & "code.txt"
Open FilePath For Output As #2
Print #2, CodeString
Close #2
MsgBox ("code.txt contains:" & CodeString)
MsgBox (Application.DefaultFilePath & "\" & mySheet.Name & "code.txt")
Erase BannerCollection
Next mySheet
End Sub
and here is the Banner type:
Public Type Banner
imagePath As String
retImagePath As String
urlPath As String
bannerTitle As String
End Type
I ended up doing a bit of a code review (oops spent too much time on the Code Review site). I'll post this here in addition to #Jeeped answer in case you get some value from it.
Option Explicit
You should specify Option Explicit at the top of each code module. What this does is tell the VBA compiler to check that every variable that you are trying to use has been declared (i.e. you've got Dim blah as String, Public blah as String or Private blah as String for each blah you're using).
If you attempt to use a variable which hasn't been declared, the compiler will give you a compilation error where the first problem occurs. This helps if you mistype a variable name, otherwise the compiler will think you are talking about something new.
Adding this to the top of your code requires a couple of declarations in your code but nothing major.
Multiple variable declaration on a single line
Don't do it. You have the following line: Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String which declares 5 variables. The first 4 are declared as variants and the last one is a String. Now your code may work like this but you were probably expecting all 5 to be Strings. Other languages I believe do operate this way but VBA doesn't.
Declare them separately like:
Dim retinaCSS As String
Dim imgCSS As String
Dim firstBannerCode As String
Dim otherBannersCode As String
Dim bannerTracking As String
Don't initialise variables unnecessarily
I see code like:
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
Now the problem with this is that you're assigning the empty string value to CodeString but then you are immediately assigning something else to it in the very next line. The risk is that you might try to use a variable before you have assigned something to it. This isn't a risk for the string type since it implicitly assigned an empty string value when it is created.
You can safely remove the first assignment to it. The danger could come from object references. Say if you have a reference to a worksheet but do not assign a worksheet to the variable before you try to use it. In any case you want to make sure that your variable has the required value before you attempt to use the value it holds.
Use Collection instead of an array
The array code is cumbersome and inflexible. VBA has a simple collection type which allows you to add and remove items to and from it without having to declare a fixed size.
You can also iterate through the contents using a For Each loop.
Here is the code I'm recommending:
Dim BannerCollection As Collection
Set BannerCollection = New Collection
' ...
For Each r In allCells.Rows
Dim thisBanner As Banner
Set thisBanner = New Banner
' ...
BannerCollection.Add thisBanner
Next r
' ...
Dim b As Banner
For Each b In BannerCollection
' do something with the banner.
Next
Now to do this, Banner must be a Class not a Type. I think it makes life a lot easier though.
Split a big method up into single purpose methods.
For instance I extracted a method as follows:
Private Function UrlEncode(ByVal text As String) As String
text = Replace(text, " ", "+")
text = Replace(text, "&", "And")
text = Replace(text, "%", "PC")
text = Replace(text, "!", "")
text = Replace(text, "£", "")
text = Replace(text, ",", "")
text = Replace(text, "'", "")
text = Replace(text, "#", "")
text = Replace(text, ".", "")
UrlEncode = text
End Function
Now this can be referenced like bannerTracking = UrlEncode(b.bannerTitle).
You are setting allCells to a distinct range of cells correctly.
Set allCells = mySheet.Range("a2", mySheet.Cells(lastRow, lastCol))
Then you loop through each row in the allCells range.
For Each r In allCells.Rows
But when you actually go to use r, it is only to use the row number.
thisBanner.imagePath = Cells(r.Row, 2).Value
r.Row is a number between 1 and 1,048,576, nothing more. There is no guarantee that Cells(r.Row, 2).Value refers to something on mySheet; only that whatever worksheet it is coming from it will using whatever worksheet's row number that corresponds to r.row. You need to define some parentage. An With ... End With block within the For ... Next and properly annotated .Range and .Cell references should suffice.
Sub createCode()
Dim myWorkbook As Workbook
Dim mySheet As Worksheet
Dim bannerCount As Integer
Dim BannerCollection() As Banner
Dim r As Range
Dim lastRow, lastCol
Dim allCells As Range
Set myWorkbook = Application.ActiveWorkbook
For Each mySheet In myWorkbook.Worksheets
With mySheet
'declare your vars outside the loop and zero/null then here if necessary.
bannerCount = 0
lastCol = .Range("a2").End(xlToRight).Column
lastRow = .Range("a2").End(xlDown).Row
Set allCells = .Range("a2", .Cells(lastRow, lastCol))
' MsgBox (mySheet.Name)
' MsgBox ("lastRow:" & lastRow & "lastCol:" & lastCol)
ReDim BannerCollection(allCells.Rows.Count)
For Each r In allCells.Rows
Dim thisBanner As Banner
thisBanner.imagePath = ""
thisBanner.retImagePath = ""
thisBanner.bannerTitle = ""
thisBanner.urlPath = ""
bannerCount = bannerCount + 1
' MsgBox (bannerCount)
thisBanner.imagePath = .Cells(r.Row, 2).Value
thisBanner.retImagePath = .Cells(r.Row, 3).Value
thisBanner.bannerTitle = .Cells(r.Row, 4).Value
thisBanner.urlPath = .Cells(r.Row, 5).Value
'MsgBox (.Cells(r.Row, 2).Value)
'MsgBox (.Cells(r.Row, 3).Value)
'MsgBox (.Cells(r.Row, 4).Value)
'MsgBox (.Cells(r.Row, 5).Value)
BannerCollection(bannerCount - 1) = thisBanner
Next r
Dim i As Variant
Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String
retinaCSS = ""
imgCSS = ""
firstBannerCode = ""
otherBannersCode = ""
bannerTracking = ""
For i = 0 To bannerCount - 1
bannerTracking = BannerCollection(i).bannerTitle
bannerTracking = Replace(bannerTracking, " ", "+")
bannerTracking = Replace(bannerTracking, "&", "And")
bannerTracking = Replace(bannerTracking, "%", "PC")
bannerTracking = Replace(bannerTracking, "!", "")
bannerTracking = Replace(bannerTracking, "£", "")
bannerTracking = Replace(bannerTracking, ",", "")
bannerTracking = Replace(bannerTracking, "'", "")
bannerTracking = Replace(bannerTracking, "#", "")
bannerTracking = Replace(bannerTracking, ".", "")
retinaCSS = retinaCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).retImagePath & "');}" & vbNewLine
imgCSS = imgCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).imagePath & "');}" & vbNewLine
If i = 0 Then
firstBannerCode = firstBannerCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
firstBannerCode = firstBannerCode & "" & vbNewLine
firstBannerCode = firstBannerCode & "</div>" & vbNewLine
Else
otherBannersCode = otherBannersCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
otherBannersCode = otherBannersCode & "" & vbNewLine
otherBannersCode = otherBannersCode & "</div>" & vbNewLine
End If
' MsgBox (BannerCollection(i).retImagePath & vbNewLine & BannerCollection(i).imagePath & vbNewLine & BannerCollection(i).bannerTitle & vbNewLine & BannerCollection(i).urlPath)
Next i
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & "/* Banners */" & vbNewLine
CodeString = CodeString & imgCSS
CodeString = CodeString & "/* Retina Banners */" & vbNewLine
CodeString = CodeString & "#media only screen and (-webkit-min-device-pixel-ratio: 2) {" & vbNewLine
CodeString = CodeString & retinaCSS
CodeString = CodeString & "}" & vbNewLine
CodeString = CodeString & "</style>" & vbNewLine
CodeString = CodeString & "<div id=" & Chr(34) & "sliderTarget" & Chr(34) & " class=" & Chr(34) & "slides" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & firstBannerCode
CodeString = CodeString & "</div>" & vbNewLine
CodeString = CodeString & "<script id=" & Chr(34) & "sliderTemplate" & Chr(34) & " type=" & Chr(34) & "text/template" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & otherBannersCode
CodeString = CodeString & "</script>"
FilePath = Application.DefaultFilePath & "\" & mySheet.Name & "code.txt"
Open FilePath For Output As #2
Print #2, CodeString
Close #2
MsgBox ("code.txt contains:" & CodeString)
MsgBox (Application.DefaultFilePath & "\" & mySheet.Name & "code.txt")
Erase BannerCollection
End With
Next mySheet
End Sub