如何使用VBA查找给定搜索词的图像结果数

时间:2017-02-21 16:42:28

标签: html excel vba excel-vba

我一直在使用Excel中的HTML,试图估算不同分辨率的常见图像。我希望得到一些动态的东西 - 用户输入一个搜索词,代码循环通过一组预定义的图像分辨率,对指定分辨率之间的搜索词的常见图像进行排序。

第一步是获得以特定分辨率返回图像数量的可靠(快速)方式。我写了这段代码:

Sub GoogleWithURL() 'requires Microsoft HTML Object Library

    Dim url As String, searchTerm As String
    Dim objIE As InternetExplorer 'special object variable representing the IE browser
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("sheet1")
    Dim currPage As HTMLDocument
    Dim xRes As Integer, yRes As Integer
    With ws
        xRes = .Range("XRes")
        yRes = .Range("YRes")
        searchTerm = .Range("search")
    End With

    'create URL to page with these image criteria
    url = WorksheetFunction.Concat("https://www.google.com/search?q=", searchTerm, _
                        "&tbm=isch&source=lnt&tbs=isz:ex,iszw:", xRes, "iszh:", yRes)

    'initiating a new instance of Internet Explorer and asigning it to objIE
    Set objIE = New InternetExplorer
    'objIE.Visible = True 'for debugging purposes

    'Google images search
    objIE.navigate url
    Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
    Set currPage = objIE.document

    'Count image results
    Set valueResult = currPage.getElementById("rg_s").getElementsByTagName("IMG")
    MsgBox WorksheetFunction.Concat("'", searchTerm, "' returns ", valueResult.Length _
    , " images @ ", xRes, "x", yRes, "px.") 'returns number of loaded images on page

    'close the browser
    On Error Resume Next 'required when the browser is visible and I close it manually half way
    objIE.Quit

End Sub

它将Internet Explorer对象导航到特定分辨率谷歌图片搜索,计算rg_s ID中的图像数量(这些是图像结果而不是横幅图像等)。然后它将该计数作为消息框返回。 (当我最终实现这一点时,我将在工作表的一列中返回值,循环显示30个不同的分辨率)

问题

此代码的主要问题是:

  • 它没有给出非常有用的计数。 分辨率很低,因为它只对已加载的图像进行计数 - 这意味着大多数搜索条件(如1920x1080或1366x768)会返回最多100张图像。

  • 很慢。对我来说,导航到页面,计算图像标签,这看起来很像在VBA中使用.Select。这就像手动方法,人类会做什么,因此效率低下。

解决方案

我可以想出一些解决这些问题的方法

  1. 解决数据/获得更有用的计数

    • 向下滚动。如果我可以加载更多图像,我可能会更好地区分。我发现尽可能向下滚动(直到'加载更多结果'按钮)给出400不是100的上限 - 如果给定分辨率至少有那么多图像那么我很开心,我会把它排在最前面。但是对问题2没有帮助。不过,我该怎么做?

    • 缩小搜索结果。如果返回100,我可以更改我发送的URL中的filetype:,例如附加filetype:png可以将返回的图像数量减半,从而在0-100范围内给出更好的传播。虽然不太理想,因为我必须为某些分辨率迭代多个文件类型,减慢代码速度,甚至不一定给我我想要的东西。

    • 使用Google(或其他搜索引擎)自己的值。我已经在各种网站上以各种形式询问了这一点,是否有任何直接来自Google的图像数据 - 即没有返回(并缓慢加载)图像本身。就像常规搜索的about 1,300,500 results in 0.03 seconds一样,仅适用于图像?如果我每次采样一个比100个结果更大的数组时使用预先计算的值,我可能会得到更详细的图片。

  2. 慢度

    • 尝试其他类型的HTTP请求。现在我打开一个Internet Explorer实例并导航到一个页面。听起来非常人类风格,我更喜欢计算机风格的请求。我的意思是,不是让我的笔记本电脑一个接一个地浏览图像,而是让谷歌的超级计算机只通过询问进行计数,而不是图像本身。不知道如何做到这一点。我知道另外两种从Excel中搜索网络的方法;网页查询和CreateObject("MSXML2.serverXMLHTTP")。不知道其中任何一个,但如果你认为它们是更好的方式,那么我会更仔细地研究它们。
  3. 摘要

    希望能够继续下去,我认为我的思路应该相当清楚。关于如何向下滚动/加载更多图像/让Google返回计数而非图像本身的实际答案将是最好的,关于应该采取什么的建议也是有用的。

2 个答案:

答案 0 :(得分:1)

你的瓶颈不在for循环中。它打开浏览器并将其指向某个位置。如果您担心时间,那么您应该抓住已经对该页面打开的浏览器,并且在您对其运行所有搜索之前不要关闭它。每次搜索至少应保存2秒。我运行了以下代码并得到了这些时间:

打开并设置资源管理器的时间:2.41秒。

计算100张照片的时间(1):0.1秒。

计算100张照片的时间(2):0.11秒。

我们的方法之间的差异是1/100秒。

此外,Google图片要求用户向下翻页,以便拨打下一张100张图片。如果您可以找到ajax或javascript语句来实现这一点,那么您将能够让它认为它已经关闭了页面。这就是为什么你只能获得100张图像的原因。

或者您可以打开浏览器,输入搜索字词,然后向下翻页,直到屏幕上显示299张图片,此时您会找到一个显示&#34;显示更多图片&#34;的按钮。然后抓住那个打开的网页。

如果您运行多个搜索字词而不是时间瓶颈在于打开和关闭浏览器,那么不计算图像。

Sub GoogleWithURL() 'requires Microsoft HTML Object Library
' https://www.google.com/search?q=St+Mary&source=lnms&tbm=isch&sa=X&ved=0ahUKEwj99ay14aPSAhWDMSYKHadiCjkQ_AUICSgC&biw=1600&bih=840
    Dim url As String
    Dim objIE As InternetExplorer 'special object variable representing the IE browser
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")
    Dim currPage As HTMLDocument
    Dim StartTime As Double, SecondsElapsed As Double

    '****************************************
    '   Hard code url to search images of St Mary
    url = "https://www.google.com/search?q=St+Mary&source=lnms&tbm=" & _
            "isch&sa=X&ved=0ahUKEwj99ay14aPSAhWDMSYKHadiCjkQ_AUICSgC&biw=1600&bih=840"

    StartTime = Timer
    Set objIE = New InternetExplorer
    objIE.Visible = True
    objIE.navigate url
    Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
    Set currPage = objIE.document
    SecondsElapsed = Round(Timer - StartTime, 2)
    Debug.Print "Time to open and set Explorer:  " & SecondsElapsed & " seconds."


    StartTime = Timer
    Set valueResult = currPage.getElementById("rg_s").getElementsByTagName("IMG")
    For Each pic In valueResult
        counter = counter + 1
    Next pic
    SecondsElapsed = Round(Timer - StartTime, 2)

    Debug.Print "Time to Count " & counter & " Photos(1):  " & SecondsElapsed & " seconds."

    counter = 0
    StartTime = Timer
    Set valueResult = currPage.getElementsByTagName("IMG")
    For Each pic In valueResult
        If InStr(1, pic.className, "rg") > 0 Then
            counter = counter + 1
        End If
    Next pic
    SecondsElapsed = Round(Timer - StartTime, 2)

    Debug.Print "Time to Count " & counter & " Photos(2):  " & SecondsElapsed & " seconds."

    On Error Resume Next 'required when the browser is visible and I close it manually half way
    objIE.Quit

End Sub

答案 1 :(得分:0)

经过几个问题,现在感觉有点明智了,我为此制作了一个UDF:

Public Function GOOGLE_COUNT(searchTerm As String, xRes As Long, yRes As Long, Optional timeout As Long = 10) As Long

    Dim url As String
    Dim objIE As InternetExplorer
    Dim currPage As HTMLDocument
    Dim stTimer As Double, tElapsed As Single
    Dim valueResult As IHTMLElementCollection

    'create URL to page with these image criteria
    url = "https://www.google.com/search?q=" & searchTerm & _
                        "&tbm=isch&source=lnt&tbs=isz:ex,iszw:" & xRes & ",iszh:" & yRes

    'initiating a new instance of Internet Explorer and asigning it to objIE
    Set objIE = New InternetExplorer

    'Google images search
    objIE.navigate url
    Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
    Set currPage = objIE.document
    Dim myDiv As HTMLDivElement: Set myDiv = currPage.getElementById("fbar")
    Dim elemRect As IHTMLRect: Set elemRect = myDiv.getBoundingClientRect
    stTimer = Timer
    'Scroll until bottom of page is in view
    Do Until elemRect.bottom > 0 Or tElapsed > timeout 'timeout after n seconds
        currPage.parentWindow.scrollBy 0, 10000
        Set elemRect = myDiv.getBoundingClientRect
        tElapsed = Timer - stTimer
    Loop
    myDiv.ScrollIntoView
    'Count the images
    Set valueResult = currPage.getElementById("rg_s").getElementsByTagName("IMG")
    GOOGLE_COUNT = valueResult.Length
    objIE.Quit

End Function

像这样工作:以1366:768图像大小搜索“圣玛丽”然后

=GOOGLE_COUNT("St. Mary", 1366, 768)

或者超时10秒(如果已经过了10秒,搜索会停止滚动,只计算加载的图像)

=GOOGLE_COUNT("St. Mary", 1366, 768, 10)

我在another question中解释滚动是如何工作的,它现在很乱,但很实用。

重要:

正如@John Muggins所指出的那样,所花费的大量时间是装载,而不是计算。特别是,打开和关闭InternetExplorer。所以要避免大量的重新计算时间;如果(像我一样)你想检查多个术语/分辨率,将此代码放在宏中,而不是函数(如果您认为我应该发布此评论,请发表评论)。 此UDF仅适用于一次性搜索

希望它有用,我想我应该重新访问这个问题来发布我得到的答案。

最后注意事项:

  1. 您的计算机(可能)没有崩溃,该功能只是计算。

  2. 对于搜索字词,请键入您在Google搜索栏中输入的内容 - 例如“Jaguar -car”返回动物的图像,而非汽车公司

  3. 结果是0-400; 0-399是实际计算的图像数量(只要你设置超时足够大 - 自动为10秒)。 400是最大值,因此在该分辨率下,该术语可能有超过400张图像。