【问题标题】:Can't execute my code without "on error resume next"没有“on error resume next”就无法执行我的代码
【发布时间】:2017-05-21 13:13:08
【问题描述】:

结合硒在VBA中编写爬虫来解析网页中不同产品的价格,我在执行时遇到问题。当它发现某些价格不存在时,它就会中断。使用“on error resume next”我可以获得完整的结果。但是,我希望在不使用“on error resume next”的情况下执行我的代码。如果不是硒,我可以使用 Length 属性来摆脱它。但是,硒不支持这一点。希望我可以在这里找到任何解决方法。

Sub Redmart_scraping()
Dim driver As New ChromeDriver
Dim posts As Object, post As Object

With driver
    .get "https://redmart.com/bakery"
    Set posts = .FindElementsByCss("li.productPreview")
End With

On Error Resume Next

For Each post In posts
    i = i + 1
    Cells(i, 1) = post.FindElementByCss("span[class^=ProductPrice__price]").Text
Next post
End Sub

【问题讨论】:

    标签: vba selenium web-scraping


    【解决方案1】:

    您可以按如下方式提取价格:

    Sub Redmart_scraping()
    Dim driver As New ChromeDriver
    Dim posts As Object
    Dim i As Long
    
    With driver
        .get "https://redmart.com/bakery"
    End With
    
    Columns("A:A").NumberFormat = "[$$-409]#,##0.00"
    
    For Each posts In driver.FindElementsByClass("productPreview")
        i = i + 1
        'Cells(i, 2) = posts.Text
        For Each Item In Split(posts.Text, vbLf)
            If InStr(1, Item, "$", vbTextCompare) > 0 Then
                If InStr(2, Item, "$", vbTextCompare) > 0 Then
                    Cells(i, 1) = Mid(Item, 2, InStr(2, Item, "$", vbTextCompare) - 2)
                Else
                    Cells(i, 1) = Right(Item, Len(Item) - 1)
                End If
    
           End If
        Next
    Next
    End Sub
    

    请注意,posts.Text 包含您需要的一件商品的所有信息。因此,除了价格,您还可以提取商品名称、折扣、客户评分、重量、折扣前价格和保证新鲜日期。取消注释'Cells(i, 2) = posts.Text 并自己查看。

    我把剩下的有趣的工作留给你。祝你好运!

    【讨论】:

    • 你是不可能的,Tehscript。你简直让我目瞪口呆。我敢打赌,您不会找到一个线程提供了如此出色的解决方案来解决 vba + selenium 发生的“下一个错误恢复”问题,至少我没有。总有一天,我会找到一个难题给你解决:)。感谢一万亿。
    • 谢谢SMth80!请随时回答您的问题!继续努力!
    • @SMth80 是的,看起来不错,但网站上有 60 个价格,而您的代码只提取了其中的 31 个。在这种情况下,您需要在代码中寻找不同的元素。
    • 我所做的不正确,但我尝试了,为此我敢于向您展示。谢谢。
    【解决方案2】:

    看起来,从接受的答案来看,您的脚本在促销价格上失败,使用 2 个属性选择器结合 OR 来设置您的原始帖子变量。那么你就没有空值来处理了。 (迟来的答案 - 在寻找资源时遇到了这个问题!)。

    Option Explicit
    Public Sub Redmart_scraping()
        Dim driver As New ChromeDriver
        Dim posts As Object, post As Object, i As Long
    
        With driver
            .get "https://redmart.com/bakery"
            Do
            Set posts = .FindElementsByCss("li.productPreview span[class^=ProductPrice__price],span[class^=ProductPrice__promo_price]")
            Loop While posts.Count = 0
        End With
    
        For Each post In posts
            i = i + 1
            Cells(i, 1) = post.Text
        Next post
    End Sub
    

    【讨论】:

    • 是的,你的人正在工作@QHarr。现在,看看我在下面的发现如何替代它。谢谢。
    【解决方案3】:

    根据文档,这就是 Florent B. 建议避免任何索引错误并使代码运行到底的方式。

    Sub ScrapePrice()
        Dim driver As New ChromeDriver, post As Object
    
        With driver
            .get "https://redmart.com/bakery"
            For Each post In .FindElementsByCss("li.productPreview")
                If Not post.FindElementByCss("span[class^='ProductPrice__price']", Raise:=False, timeout:=0) Is Nothing Then
                    R = R + 1: Cells(R, 1) = post.FindElementByCss("span[class^='ProductPrice__price']").Text
                End If
            Next post
        End With
    End Sub
    

    【讨论】:

    • 在我的情况下,内容尚未加载到此行 For Each post In .FindElementsByCss("li.productPreview") 被点击所以它跳到最后。至少从我的角度来看,它需要延迟,直到结果出现/理想情况下超时(是的,我很懒,没有包括一个!)。但这就是他谁是 Selenium Basic 的方式?你还有什么可爱的小把戏?看,这就是为什么我应该跟踪你的旧问题/答案! +
    • Sooooo....... 在您的阅读中,您是否找到了在 VBA selenium 中拉出网页但阻止 JS 执行(即脚本运行)的方法?我认为 .SetPreference "javascript.enabled", False 会这样做,但它似乎不像那样工作。看起来 like 也许我需要一个单独的配置文件并加载它.....不确定。
    • 我是有史以来最差的读者!!我真的没想到@QHarr。我有空的时候一定会努力的。在循环中找到您总是很高兴。
    【解决方案4】:
    Sub Testing()
        Dim driver As New WebDriver
        Dim posts As Object, post As Object
        dim this as string, that as string
    
        Set driver = New WebDriver
        driver.Start "Phantomjs", "https://redmart.com"
        driver.get "/bakery"
        Set posts = driver.FindElementsByXPath("//div[@class='productDescriptionAndPrice']")
        On Error Resume Next
        For Each post In posts
            i = i + 1
            If Isnull(post.FindElementByXPath(".//h4/a").Text) = True Then
                this = ""
            Else
                this = post.FindElementByXPath(".//h4/a").Text
            End if
            Cells(i, 1) = this
    
            if IsNull(post.FindElementByXPath(".//span[@class='ProductPrice__price___3BmxE']").Text) = True then
                that = ""
            else
                that = post.FindElementByXPath(".//span[@class='ProductPrice__price___3BmxE']").Text
            end if
            Cells(i, 2) = that
        Next post
    End Sub
    

    古老的“空指针”令人讨厌。检查空值很重要。有一个更好的方法可以做到这一点,但你明白了(双关语)

    【讨论】:

    • 感谢 Doug Coats 先生的回答。您提供的代码不能回避问题。它在达到第一个空值时中断。但是,您提供的代码会一直运行,直到值用完为止,因为“on error resume next”也在那里。
    • @SMth80 then take out on Error resume next then?
    • 我首先做到了,先生并给了你反馈。再次感谢。
    • 我以前从未玩过这些东西,所以这纯粹是一个问题,而不是批评,但Isnull(post.FindElementByXPath(".//h4/a").Text) 不需要是Isnull(post.FindElementByXPath(".//h4/a")) - 即FindElementByXPath 不需要是可能是Null 的东西,然后尝试查找Null.Text 属性将崩溃而不是返回Null
    • 我认为您需要同时检查两者的空值,实际上并没有考虑到这一点。我打算安装 selenium 只是为了 100% 确定,但是嗯。
    【解决方案5】:

    On Error Resume Next 只是跳过任何运行时错误 - 你不想在一般情况下应用它是对的。

    如果您不能从一开始就阻止这些错误的发生(例如,通过测试null),您应该只为实际可以创建的那部分代码激活On Error Resume Next这些错误并在使用On Error Goto 0 后立即将其停用。

    在这些On Error 块内,您还可以使用Err 对象显式测试发生了哪个 错误(因此将被忽略)。这样,您可以重新引发所有您没有预料到的错误,而只忽略您预期的错误。 注意:必须在On Error 块之外引发错误,但离开块将重置Err 对象。因此,您必须将所有必要的错误信息保存在另一个变量(或多个变量)中才能在离开 On Error 块后访问它。

    恐怕对于 VBA 提供的错误处理的一般混乱设计,没有更好的方法。

    【讨论】:

      猜你喜欢
      • 2017-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多