I have built an Excel application, which gets all the titles of books from Amazon.com, which it is asked to do and scrapes the following data out of them:
- Book Title
- Author
- Price
What do you need to run the app?
3 worksheets in Excel, named as in the picture:
Then in tblInput
, make sure to write some keywords in range A2:A4
and execute the Main
function. No dependencies or additional Excel libraries to be added.
The code is also in GitHub here. I wrote a blog post about it - http://www.vitoshacademy.com/vba-data-scraping-from-internet-with-excel-part-2/
AmazonInternet
Public Function PageWithResultsExists(appIE As Object, keyword As String) As Boolean
On Error GoTo PageWithResultsExists_Error
Dim allData As Object
Set allData = appIE.document.getElementById("s-results-list-atf")
PageWithResultsExists = True
IeErrors = 0
On Error GoTo 0
Exit Function
PageWithResultsExists_Error:
WaitSomeMilliseconds
IeErrors = IeErrors + 1
Select Case Err.Number
Case 424
If IeErrors > MAX_IE_ERRORS Then
PageWithResultsExists = False
IeErrors = 0
Else
LogMe "PageWithResultsExists", IeErrors, keyword, IeErrors
PageWithResultsExists appIE, keyword
End If
Case Else
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Select
End Function
Public Function MakeUrl(i As Long, keyword As String) As String
MakeUrl = "https://www.amazon.com/s/ref=sr_pg_" & i & "?rh=i%3Aaps%2Ck%3A" & keyword & "&page=" & i & "&keywords=" & keyword
End Function
Public Sub Navigate(i As Long, appIE As Object, keyword As String)
Do While appIE.Busy
DoEvents
Loop
With appIE
.Navigate MakeUrl(i, keyword)
.Visible = False
End With
Do While appIE.Busy
DoEvents
Loop
End Sub
ConstValues
Public IeErrors As Long
Public Const MAX_IE_ERRORS = 10
Public Const IN_PRODUCTION = False
ExcelRelated
Public Function GetNextKeyWord() As String
With tblInput
Dim lastRowB As Long
lastRowB = lastRow(.Name, 2) + 1
GetNextKeyWord = Trim(.Cells(lastRowB, 1))
If Len(GetNextKeyWord) <> 0 Then .Cells(lastRowB, 2) = Now
End With
End Function
Public Sub WriteFormulas()
Dim i As Long
With tblInput
For i = lastRow(.Name) To 2 Step -1
.Cells(i, 3).FormulaR1C1 = "=COUNTIF(Summary!C[1],Input!RC[-2])"
.Cells(i, 4).FormulaArray = "=MAX(IF(Summary!C=RC[-3],Summary!C[-1]))"
FormatUSD .Cells(i, 4)
.Cells(i, 5).FormulaArray = "=AVERAGE(IF(Summary!C[-1]=Input!RC[-4],Summary!C[-2]))"
FormatUSD .Cells(i, 5)
Next i
End With
End Sub
Public Sub FixWorksheets()
OnStart
With tblInput
.Range("B1") = "Start Time"
.Range("C1") = "Count"
.Range("D1") = "Max"
.Range("E1") = "Average"
End With
With tblSummary
.Range("A1") = "Title"
.Range("B1") = "Author"
.Range("C1") = "Price"
.Range("D1") = "Keyword"
End With
Dim ws As Worksheet
For Each ws In Worksheets
ws.Columns.AutoFit
Next ws
OnEnd
End Sub
Public Sub FormatUSD(myRange As Range)
myRange.NumberFormat = "_-[$$-409]* #,##0.00_ ;_-[$$-409]* -#,##0.00 ;_-[$$-409]* ""-""??_ ;_-@_ "
End Sub
Public Sub CleanWorksheets()
tblRawData.Cells.Delete
tblSummary.Cells.Delete
tblInput.Columns("B:F").Delete
End Sub
Public Function GetNthString(n As Long, myRange As Range) As String
Dim i As Long
Dim myVar As Variant
myVar = Split(myRange, vbCrLf)
For i = LBound(myVar) To UBound(myVar)
If Len(myVar(i)) > 0 And n = 0 Then
GetNthString = myVar(i)
Exit Function
ElseIf Len(myVar(i)) > 0 Then
n = n - 1
End If
Next i
End Function
Public Function GetPrice(myRange As Range) As String
Dim i As Long
Dim myVar As Variant
myVar = Split(myRange, "$")
If UBound(myVar) > 0 Then
GetPrice = Mid(myVar(1), 1, InStr(1, myVar(1), " "))
Else
GetPrice = ""
End If
End Function
Public Sub WriteToExcel(appIE As Object, keyword As String)
If IN_PRODUCTION Then On Error GoTo WriteToExcel_Error
Dim allData As Object
Set allData = appIE.document.getElementById("s-results-list-atf")
Dim book As Object
Dim myRow As Long
For Each book In allData.getElementsByClassName("a-fixed-left-grid-inner")
With tblRawData
myRow = lastRow(.Name) + 1
On Error Resume Next
.Cells(myRow, 1) = book.innertext
.Cells(myRow, 2) = keyword
On Error GoTo 0
End With
Next
IeErrors = 0
On Error GoTo 0
Exit Sub
WriteToExcel_Error:
IeErrors = IeErrors + 1
If IeErrors > MAX_IE_ERRORS Then
Debug.Print "Error " & Err.Number & " (" & Err.Description & ") in procedure WriteToExcel, line " & Erl & "."
Else
LogMe "WriteToExcel", IeErrors, keyword, IeErrors
WriteToExcel appIE, keyword
End If
End Sub
Public Sub RawDataToStructured(keyword As String, firstRow As Long)
Dim i As Long
For i = firstRow To lastRow(tblRawData.Name)
With tblRawData
If InStr(1, .Cells(i, 1), "Sponsored ") < 1 Then
Dim title As String
title = GetNthString(0, .Cells(i, 1))
Dim author As String
author = GetNthString(1, .Cells(i, 1))
Dim price As String
price = GetPrice(.Cells(i, 1))
If Not IsNumeric(price) Or price = "0" Then price = ""
Dim currentRow As String: currentRow = lastRow(tblSummary.Name) + 1
With tblSummary
.Cells(currentRow, 1) = title
.Cells(currentRow, 2) = author
.Cells(currentRow, 3) = price
.Cells(currentRow, 4) = keyword
End With
End If
End With
Next i
End Sub
Public Function lastRow(wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
Set ws = Worksheets(wsName)
lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row
End Function
General
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Public Sub OnEnd()
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.AskToUpdateLinks = True
Application.DisplayAlerts = True
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
Application.StatusBar = False
End Sub
Public Sub OnStart()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.AskToUpdateLinks = False
Application.DisplayAlerts = False
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
ActiveWindow.View = xlNormalView
End Sub
Public Sub LogMe(ParamArray arg() As Variant)
Debug.Print Join(arg, "--")
End Sub
Public Sub PrintMeUsefulFormula()
Dim strFormula As String
Dim strParenth As String
strParenth = """"
strFormula = Selection.FormulaR1C1
strFormula = Replace(strFormula, """", """""")
strFormula = strParenth & strFormula & strParenth
Debug.Print strFormula
End Sub
Public Sub WaitSomeMilliseconds(Optional Milliseconds As Long = 1000)
Sleep Milliseconds
End Sub
StartUp
Public Sub Main()
If IN_PRODUCTION Then On Error GoTo Main_Error
CleanWorksheets
Dim keyword As String: keyword = GetNextKeyWord
While keyword <> ""
Dim appIE As Object
Set appIE = CreateObject("InternetExplorer.Application")
LogMe keyword
Dim nextPageExists As Boolean: nextPageExists = True
Dim i As Long: i = 1
Dim firstRow As Long: firstRow = lastRow(tblRawData.Name) + 1
While nextPageExists
WaitSomeMilliseconds
Navigate i, appIE, keyword
nextPageExists = PageWithResultsExists(appIE, keyword)
If nextPageExists Then WriteToExcel appIE, keyword
i = i + 1
Wend
LogMe Time, keyword, "RawDataToStructured"
RawDataToStructured keyword, firstRow
keyword = GetNextKeyWord
WaitSomeMilliseconds 4000
appIE.Quit
Wend
FixWorksheets
WriteFormulas
LogMe "Program has ended!"
On Error GoTo 0
Exit Sub
Main_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure Main, line " & Erl & "."
End Sub
In general, I probably could have done it with some OOP/classes, but I have considered not to.
Thanks!