My goal is to strip all Google search results from page 1 with VBA to Excel. Until so far I managed to strip the first result. The head, link and date are stored in cells 4, 5, 6. I now have to make a loop for the other li's, but I can't get it straight. Also the function that stores the date isn't very optimal coded I think. Anyone who knows the answer?
Sub XMLHTTP()
Dim url As String, lastRow As Long
Dim XMLHTTP As Object, html As Object, objResultDiv As Object, objH3 As Object, link As Object, Objdatum As Object, Ddatum As Object
Dim start_time As Date
Dim end_time As Date
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Dim cookie As String
Dim result_cookie As String
start_time = Time
Debug.Print "start_time:" & start_time
For i = 2 To lastRow
url = "https://www.google.co.in/search?q=" & Cells(i, 3) & "Skipr" & "&rnd=" & WorksheetFunction.RandBetween(1, 10000)
Set XMLHTTP = CreateObject("MSXML2.serverXMLHTTP")
XMLHTTP.Open "GET", url, False
XMLHTTP.setRequestHeader "Content-Type", "text/xml"
XMLHTTP.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0"
XMLHTTP.send
Set html = CreateObject("htmlfile")
html.body.innerHTML = XMLHTTP.ResponseText
Set objResultDiv = html.getelementbyid("rso")
Set objH3 = objResultDiv.getelementsbytagname("H3")(0)
Set link = objH3.getelementsbytagname("a")(0)
Set Objdatum = objResultDiv.getelementsbytagname("span")(2)
str_text = Replace(link.innerHTML, "<EM>", "")
str_text = Replace(str_text, "</EM>", "")
dat_text = Objdatum.innerHTML
Cells(i, 4) = str_text
Cells(i, 5) = link.href
Cells(i, 6) = dat_text
DoEvents
Next
end_time = Time
Debug.Print "end_time:" & end_time
Debug.Print "done" & "Time taken : " & DateDiff("n", start_time, end_time)
MsgBox "done" & "Time taken : " & DateDiff("n", start_time, end_time)
End Sub
You need to iterate the collections returned to you by your getelementsbytagname call instead of returning only the first element with the array index (0)
I had a similar project, below are some tips & my approach for your reference, it might assist you in working & maintaining the code in the future:
First, Instead of using CreateObject I prefer to reference the Object Libraries that expose the COM objects, this gives me the ability to browse the functions and properties of each object F2 and gives me code completion (speed & less bugs) within VBA editor (F7 takes you back to code view).
Giving me documentation and code completion:
Also, use these const for clarity
'see ready state : https://msdn.microsoft.com/en-us/library/ie/ms534361(v=vs.85).aspx
Const READYSTATE_UNINITIALIZED = 0
Const READYSTATE_LOADING = 1
Const READYSTATE_LOADED = 2
Const READYSTATE_INTERACTIVE = 3
Const READYSTATE_COMPLETE = 4
Finally, using DOMDocument60 to parse the XML into a Document Object Model in memory.
and MSHTML.HTMLDocument to parse the HTML document and iterate the table rows.
Below is code where I iterate all returned rows from a table within a html document embedded in the initial XML document returned by the webserver:
Dim xmlDoc As DOMDocument60
Set xmlDoc = GetXMLDocument("http://www.nbg.ge/rss.php")
'extract publication date
Debug.Print xmlDoc.getElementsByTagName("pubDate")(0).Text
'unwrap html document from CDATA in "//item/description" element
Dim htmlDoc As New MSHTML.HTMLDocument
htmlDoc.body.innerHTML = xmlDoc.SelectNodes("//item/description")(0).Text
'extract table data from html document
Dim tr As IHTMLElement, td As IHTMLElement
For Each tr In htmlDoc.getElementsByTagName("tr")
For Each td In tr.Children
'each cell in current row
Debug.Print " " & td.innerHTML
Next td
'next row
Debug.Print "-----"
Next tr
Sample Data returned by webservice I was calling:
<rss version="2.0">
<channel>
<title>RSS NBG Currency Rates</title>
<link>https://www.nbg.gov.ge/index.php?m=236&lang=geo</link>
<description>Currency Rates</description>
<language>geo</language>
<copyright>Copyright 2015, NBG</copyright>
<pubDate>Wed, 29 Apr 2015 12:39:50 +0400</pubDate>
<lastBuildDate>Wed, 29 Apr 2015 12:39:50 +0400</lastBuildDate>
<managingEditor>alex#proservice.ge</managingEditor>
<webMaster>alex#proservice.ge</webMaster>
<item>
<title>Currency Rates 2015-04-29</title>
<link>https://www.nbg.gov.ge/index.php?m=236&lang=geo</link>
<description>
<![CDATA[
<table border="0">
<tr>
<td>AED</td>
<td>10 არაბეთის გაერთიანებული საამიროების დირჰამი</td>
<td>6.2858</td>
<td><img src="https://www.nbg.gov.ge/images/green.gif"></td>
<td>0.0640</td> </tr><tr> <td>AMD</td> <td>1000 სომხური დრამი</td>
<td>4.8676</td>
<td><img src="https://www.nbg.gov.ge/images/green.gif"></td>
<td>0.0414</td>
</tr>
</table>
]]>
</description>
<pubDate>Wed, 29 Apr 2015 12:39:50 +0400</pubDate>
<guid>
https://www.nbg.gov.ge/index.php?m=236&lang=geo&date=2015-04-29
</guid>
</item>
</channel>
</rss>
and the function that actually gets the document from the webserver (only works if you added the references as shown in above pictures)
Function GetXMLDocument(url As String) As MSXML2.DOMDocument60
Dim xhr As New XMLHTTP60
Dim doc As New DOMDocument60
Dim msg As String
With xhr
.Open bstrMethod:="GET", bstrUrl:=url, varAsync:=False
On Error GoTo SendError
.send
On Error GoTo 0
'http status codes - http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
'200 = SUCCESS - OK
If .readyState = READYSTATE_COMPLETE And .Status = 200 Then
'Debug.Print .responseText
doc.LoadXML (.responseText)
Else
msg = "Error" & vbNewLine & "Ready state: " & .readyState & _
vbNewLine & "HTTP request status: " & .Status
GoTo Error
End If
End With
Set GetXMLDocument = doc
Exit Function
SendError:
'by default access to data source accross internet dissabled
'go to internet options & under security>custom level>Misc>access data sources accross domains> enable
'see: http://stackoverflow.com/a/17402920
MsgBox "Make sure access data sources accross domains is enabled under internet options>security>custom", vbOKOnly, "Could not send request to server"
Error:
MsgBox msg, vbOKOnly, "Unexpected Error"
End Function
Related
I am getting the below error when i run the macro in excel...
Run-time error '91': Object variable or with block variable not set
The error occurs on the line str_text = Replace(link.innerHTML, "<EM>", "")
{
Sub XMLHTTP()
Dim url As String, lastRow As Long
Dim XMLHTTP As Object, html As Object, objResultDiv As Object, objH3 As Object, link As Object
Dim start_time As Date
Dim end_time As Date
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Dim cookie As String
Dim result_cookie As String
start_time = Time
Debug.Print "start_time:" & start_time
For i = 2 To lastRow
url = "https://www.google.co.in/search?q=" & Cells(i, 1) & "&rnd=" & WorksheetFunction.RandBetween(1, 10000)
Set XMLHTTP = CreateObject("MSXML2.serverXMLHTTP")
XMLHTTP.Open "GET", url, False
XMLHTTP.setRequestHeader "Content-Type", "text/xml"
XMLHTTP.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0"
XMLHTTP.send
Set html = CreateObject("htmlfile")
html.body.innerHTML = XMLHTTP.ResponseText
Set objResultDiv = html.getelementbyId("rso")
Set objH3 = objResultDiv.getelementsbyTagName("H3")(0)
Set link = objH3.getelementsbyTagName("a")(0)
str_text = Replace(link.innerHTML, "<EM>", "")
str_text = Replace(str_text, "</EM>", "")
Cells(i, 2) = str_text
Cells(i, 3) = link.href
DoEvents
Next
end_time = Time
Debug.Print "end_time:" & end_time
Debug.Print "done" & "Time taken : " & DateDiff("n", start_time, end_time)
MsgBox "done" & "Time taken : " & DateDiff("n", start_time, end_time)
End Sub
}
Can any one give solution for this?
You have a number of problems with your code.
You didn't let us know where the error was occurring.
As Chronocidal pointed out, your procedure name is the same as a variable name... not good.
You also have 2 variables undeclared and 2 other variables declared but not used. Add Option Explicit to the top of [every] module [forever] so VBA will "force" you to properly declare & refer to variables, objects, properties, etc.
For some reason there are {Curly Braces} around your code.
These are careless oversights showing lack of attention to detail, but I think what's causing the error in this case is the "query string data" on your worksheet (starting in cell A2). One or more of the cells are either blank or contain characters that the Google query "doesn't like".
You can determine which ones by adding this line: MsgBox url ***just after* the line with url = "https://www.... line.**
When the message box pops up, look at the query string and see if it's missing something, specifically right after q=.
Your Sub has the same name as one of your Objects: XMLHTTP - Excel probably can't tell what's what, because you have two things with the same name.
Also, you haven't declared str_text or i
I have a list of local webpages (over 9000) which I want to parse with Excel VBA.
I use Office 2013 with IE 11 on:
a Windows 7 Enterprise Pro x64, 16 GB RAM, i7 - Processor but also on
a Windows 8.1 Enterprise x64, 12 GB RAM, i7 - Processor
The problem on both machiens is that after successfuly parsing about 70-80 pages, the programm suddenly fails to load the next webpage into IE. It gets "stuck" so to say (see comment in the code below). If I reset the programm, then it can parse without problen again about 70-80 profiles after "failing" again.
[EDIT: I'm sorry, I posted by mistake the wrong code. Here is the corrected
version]
Here is a part of the code:
<!-- language: lang-HTML -->
Sub ImportFromWebpage()
'GLOBAL VARIABLES
Dim html As HTMLDocument
Dim CurrentRowPosition, ProfileNumber, TotalProfiles As Integer
Dim TempProfileID As String
Dim profileRange, posCounter, currentProfile As Range
Set profileRange = Worksheets("List_of_Files").Range("B2:B20000")
ProfileNumber = 519
CurrentRowPosition = 520
TotalProfiles = Application.WorksheetFunction.CountA(profileRange)
'MsgBox "TotalProfiles = " & TotalProfiles
'VARIABLES NEEDED FOR PARSING HERE
'ELEMENTS
Dim firstIHTMLElmt, secondIHTMLElmt, thirdIHTMLElmt As IHTMLElement
Dim firstTempIHTMLElmt, secondTempIHTMLElmt, thirdTempIHTMLElmt, fourthTempIHTMLElmt, fiftTempIHTMLElmt As IHTMLElement
'COLLECTIONS
Dim firstIHTMLEColl, secondIHTMLEColl, thirdIHTMLEColl As IHTMLElementCollection
Dim firstTempIHTMLEColl, secondTempIHTMLEColl, thirdTempIHTMLEColl, fourthTempIHTMLEColl, fifthTempIHTMLEColl As IHTMLElementCollection
Dim ie As InternetExplorerMedium
Set ie = New InternetExplorerMedium
ie.Visible = False
'FROM HERE LOOPING
For startNumber = 1 To TotalProfiles
Application.StatusBar = "Loading profile " & ProfileNumber & " from a total of " & TotalProfiles & " profiles"
'Set currentProfile = Worksheets("List_of_Files").Range("J" & CurrentRowPosition) // OLD
Set currentProfile = Worksheets("List_of_Files").Range("B" & CurrentRowPosition)
ie.navigate currentProfile
Application.StatusBar = "Loading profile: " & ProfileNumber & "; file location: " & currentProfile
Do While ie.READYSTATE <> READYSTATE_COMPLETE
DoEvents
Loop
Application.StatusBar = "Storing " & currentProfile & " information into HTMLElement"
Set html = ie.document
Set ie = Nothing
[code, code, code, code ...]
Application.Wait (Now + TimeValue("0:00:02"))
Next startNumber
Set html = Nothing
ie.Quit
Set ie = Nothing
MsgBox "Done parsing all profiles!"
End Sub
Here is a screenshot from the Windows 8.1 task manager AFTER failing to load:
Dose someone have any clue about why this is happening? Not only on one machiene, but on both.
I an not very experience with programming and even less with VBA so any help would be much appreciated.
This solution proved to be a good one in my case. Don't know whether this is the best solution but it work very good for me.
put IE declaration before the loop to initiate an instance of Internet Explorer; this is the only instance which will be used (the link is just going to be refreshed within this instance)
set html = Nothing within the loop
set ie = Nothing outside of the loop, so that only the link may be refreshed without restarting IE
ie.Quit only after parsing all >9000 webpages (so outside of the loop)
Hope it helps others with the same problem.
Sub ImportFromWebpage()
'GLOBAL VARIABLES
Dim html As HTMLDocument
Dim CurrentRowPosition, ProfileNumber, TotalProfiles As Integer
Dim TempProfileID As String
Dim profileRange, posCounter, currentProfile As Range
Set profileRange = Worksheets("List_of_Files").Range("B2:B20000")
ProfileNumber = 1
CurrentRowPosition = 2
TotalProfiles = Application.WorksheetFunction.CountA(profileRange)
'MsgBox "TotalProfiles = " & TotalProfiles
Dim ie As InternetExplorerMedium
Set ie = New InternetExplorerMedium
ie.Visible = False
'FROM HERE LOOPING
For startNumber = 1 To TotalProfiles
Application.StatusBar = "Loading profile " & ProfileNumber & " from a total of " & TotalProfiles & " profiles"
'Set currentProfile = Worksheets("List_of_Files").Range("J" & CurrentRowPosition) // OLD
Set currentProfile = Worksheets("List_of_Files").Range("B" & CurrentRowPosition)
ie.navigate currentProfile
Application.StatusBar = "Loading profile: " & ProfileNumber & "; file location: " & currentProfile
Do While ie.READYSTATE <> READYSTATE_COMPLETE
DoEvents
Loop
Application.StatusBar = "Storing " & currentProfile & " information into HTMLElement"
Set html = ie.document
[code, code, code, code ...]
Set html = Nothing
Application.Wait (Now + TimeValue("0:00:02"))
Next startNumber
Set ie = Nothing
ie.Quit
MsgBox "Done parsing all profiles!"
End Sub
Sub DownloadFile()
Dim myURL As String
myURL = "http://data.bls.gov/timeseries/LNS14000000"
Dim WinHttpReq As Object
Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
WinHttpReq.Open "GET", myURL, False, "username", "password"
WinHttpReq.send
myURL = WinHttpReq.responseBody
If WinHttpReq.Status = 200 Then
Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write WinHttpReq.responseBody
oStream.SaveToFile "C:\Downloads\abc.xlsx", 2
oStream.Close
End If
End Sub
I am trying to download data using VBA and found this code running pretty well. The webpage URL from which I am trying to download data is the one I have used in the code. Please take a moment and open the webpage as the Excel file I am trying to download is linked in an image and so I am not able to find the URL to download the file from that image. Please advice. Thanks.
You might be able to hit the form target directly with a POST (action="/pdq/SurveyOutputServlet") but it is expecting a post string of the <input> elements together with their values. Most if not all of these input elements have been filled out for you simply by going to that page. All you need to do is collect and concatenate them into a post string to be shoved back at the form.
Option Explicit
'base web page
Public Const csBLSGOVpg = "http://data.bls.gov/timeseries/LNS14000000"
'form's action target
Public Const csXLSDLpg = "http://data.bls.gov/pdq/SurveyOutputServlet"
Sub mcr_Stream_Buyer_Documents()
Dim xmlDL As New MSXML2.ServerXMLHTTP60, xmlBDY As New HTMLDocument, adoFILE As Object
Dim xmlSend As String, strFN As String, f As Long, i As Long
With xmlDL
.SetTimeouts 5000, 5000, 15000, 25000
'start by going to the base web page
.Open "GET", csBLSGOVpg, False
.setRequestHeader "Content-Type", "text/javascript"
.send
If .Status <> "200" Then GoTo bm_Exit
'get the source HTML for examination; zero the post string var
xmlBDY.body.innerHTML = .responseText
xmlSend = vbNullString
'loop through the forms until you find the right one
'then loop through the input elements and construct a post string
For f = 0 To xmlBDY.getElementsByTagName("form").Length - 1
If xmlBDY.getElementsByTagName("form")(f).Name = "excel" Then
With xmlBDY.getElementsByTagName("form")(f)
For i = 0 To .getElementsByTagName("input").Length - 1
xmlSend = xmlSend & Chr(38) & _
.getElementsByTagName("input")(i).Name & Chr(61) & _
.getElementsByTagName("input")(i).Value
Next i
xmlSend = "?.x=5&.y=5" & xmlSend
End With
Exit For
End If
Next f
'Debug.Print xmlSend 'check the POST string
'send the POST string back to the form's action target
.Open "POST", csXLSDLpg, False
xmlDL.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
xmlDL.send xmlSend
If xmlDL.Status <> "200" Then GoTo bm_Exit
'pick up the response as a stream and save it as a .XLSX
strFN = Environ("USERPROFILE") & "\Documents\LNS14000000" & Format(Date, "yyyymmdd") & ".xlsx"
On Error Resume Next
Kill strFN
On Error GoTo 0
Set adoFILE = CreateObject("ADODB.Stream")
adoFILE.Type = 1
adoFILE.Open
adoFILE.Write .responseBody
adoFILE.SaveToFile strFN, 2
Set adoFILE = Nothing
End With
Set xmlBDY = Nothing
Set xmlDL = Nothing
Exit Sub
bm_Exit:
Debug.Print Err.Number & ":" & Err.Description
End Sub
This is pretty minimalist but it is all that you need. There is at least one non-standard input element that does not have a name but I elected to send its value back anyway. I did not sequentially remove things until it broke; I just built the POST string given what I retrieved and sent it back.
LNS1400000020150916.xlsx
You will probably be moving this code to some sort of loop. Adjust the receiving file name accordingly. Each new page should adjust its own form input elements accordingly.
Once response is stored in an HTMLDocument object you can use a CSS selector of
#download_xlsx
The "#" means id.
You can then click on this element
htmlDocument.querySelector("#download_xlsx").Click
VBA:
Option Explicit
Public Sub DownloadFile()
Dim ie As New InternetExplorer
With ie
.Visible = True
.navigate "https://data.bls.gov/timeseries/LNS14000000"
While .Busy Or .readyState < 4: DoEvents: Wend
.document.querySelector("#download_xlsx").Click
.Quit
End With
End Sub
Other:
You could even target the form and submit:
.document.forms("excel").submit
This triggers the POST request mentioned in the other answer (which is an awesome answer btw).
I am running the infamous "google search & return" code but my vba stops after bout 80 records. then I have to restart. Can anyone see what is wrong in here? Sometime I have to wait until the code will allow itself to run again.
Sub XMLHTTP()
Dim url As String, lastRow As Long
Dim XMLHTTP As Object, html As Object, objResultDiv As Object, objH3 As object, link As Object
Dim start_time As Date
Dim end_time As Date
Dim rngCt As Long
Dim cookie As String
Dim result_cookie As String
lastRow = Range("A" & Rows.Count).End(xlUp).Row
start_time = Time
Debug.Print "start_time:" & start_time
Set XMLHTTP = CreateObject("MSXML2.serverXMLHTTP")
Set html = CreateObject("htmlfile")
For rngCt = 2 To lastRow
url = "https://www.google.co.in/search?q=" & Cells(rngCt, 1) & "&rnd=" & WorksheetFunction.RandBetween(1, 10000)
XMLHTTP.Open "GET", url, False
XMLHTTP.setRequestHeader "Content-Type", "text/xml"
XMLHTTP.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0"
XMLHTTP.send
DoEvents
html.body.innerHTML = XMLHTTP.ResponseText
Set objResultDiv = html.getelementbyid("rso")
Set objH3 = objResultDiv.getelementsbytagname("H3")(0)
Set link = objH3.getelementsbytagname("a")(0)
str_text = Replace(Replace(link.innerHTML, "", ""), "", "")
Cells(rngCt, 2) = str_text
Cells(rngCt, 3) = link.href
DoEvents
Set objResultDiv = Nothing
Set objH3 = Nothing
Set link = Nothing
DoEvents
Next rngCt
end_time = Time
Debug.Print "end_time:" & end_time
Debug.Print "done" & "Time taken : " & DateDiff("n", start_time, end_time)
MsgBox "done" & "Time taken : " & DateDiff("n", start_time, end_time)
End Sub
"Object variable or with block variable set" is a "null reference error" and it means that you are trying to use a property or method of an object when the object itself is null (Nothing in VBA).
For instance, if I have a Person object and it has properties Name, Age, Gender, Weight, and Height and it has a SayMyName() method, then I could use it like this:
Dim aPerson As Person
Set aPerson = New Person()
...
Then later, you could use its properties or methods like this:
MsgBox(aPerson.Name)
However, if aPerson was null (Nothing), then you would get the null reference error you got:
Set aPerson=Nothing
MsgBox(aPerson.Name) 'Null ref error will happen here.
The only way you're going to figure out what is going on here is if you can observe the state of your variables when the error happens. Try this:
In the VBA editor, go to "View" and select "Locals Window". Run your code, and then when it breaks on the error, look at what's in your "Locals" window. Hopefully, you'll be able to figure out which variable is Nothing and causing the null reference error.
Is it possible to write a macro or vba which will image search the cell content in row A and return the image's url? Ive been able to return the first result in the past or the first search result but i can't figure out how to make one that will return a relevant image such as:
http://www.gamexchange.co.uk/images/pictures/products/ps3/littlebigplanet-3-ps3-(product-photography).jpg?v=b13dfc3644d28cd2cff7d5729a35fc5e
Any help would be appreciated, as i think i've exhausted the search function now.
Thank you.
Here is the code I've used which doesn't work as desired.
Sub XMLHTTP()
Dim url As String, lastRow As Long
Dim XMLHTTP As Object, html As Object, objResultDiv As Object, objH3 As Object, link As Object
Dim start_time As Date
Dim end_time As Date
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Dim cookie As String
Dim result_cookie As String
start_time = Time
Debug.Print "start_time:" & start_time
For i = 2 To lastRow
url = "https://www.google.co.in/search?q=" & Cells(i, 1) & "&rnd=" & WorksheetFunction.RandBetween(1, 10000)
Set XMLHTTP = CreateObject("MSXML2.serverXMLHTTP")
XMLHTTP.Open "GET", url, False
XMLHTTP.setRequestHeader "Content-Type", "text/xml"
XMLHTTP.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0"
XMLHTTP.send
Set html = CreateObject("htmlfile")
html.body.innerHTML = XMLHTTP.ResponseText
Set objResultDiv = html.getelementbyid("rso")
Set objH3 = objResultDiv.getelementsbytagname("H3")(0)
Set link = objH3.getelementsbytagname("a")(0)
str_text = Replace(link.innerHTML, "<EM>", "")
str_text = Replace(str_text, "</EM>", "")
Cells(i, 2) = str_text
Cells(i, 3) = link.href
DoEvents
Next
end_time = Time
Debug.Print "end_time:" & end_time
Debug.Print "done" & "Time taken : " & DateDiff("n", start_time, end_time)
MsgBox "done" & "Time taken : " & DateDiff("n", start_time, end_time)
End Sub
The problem is that every search is going to take you to a different domain, with a completely different HTML structure.
There is no reliable way to do this and bring in the correct picture, because you cannot possibly know in advance the structure of the returned HTML which you need to parse for the image... Of course you could try to return, for instance, the first image on each page, but since the pages will likely contain many images which are irrelevant to your search, that is not going to be very reliable.
One alternative would be to do what you are currently doing to obtain the str_text and the link.href to columns B and C of your spreadsheet, and then run a second XMLHTTP request to a URL of the following structure:
url = "https://www.google.co.in/search?q=" & Cells(i, 1) & "&source=lnms&tbm=isch&sa=X" & "&rnd=" & WorksheetFunction.RandBetween(1, 10000)
This will return a Google Image search. Again, this will not be perfect since the application has no way of knowing which is the "best" picture to return, but you could more reliably return the first image since these results will be more likely relevant to your search term.