【问题标题】:VBA function IFERROR THEN GO TO LINE XVBA 函数 IFERROR THEN GO TO LINE X
【发布时间】:2015-04-13 17:27:48
【问题描述】:

我在使用 VBA 中的 ON ERROR GOTO 函数时遇到了一些问题。实际上,我不确定我想要的是否真的可能。

我正在不同网站收集一些信息,完整代码在这里

Dim IE As InternetExplorer
Dim html As HTMLDocument
Set IE = New InternetExplorer
Dim Ano As Long
Dim offsetCount As Long
Dim URL As String
Dim NUMERO As String

Ano = 2012
offsetCount = 2

Do While Ano >= 2005

    Range("E1").Value = Ano
    Range("D2").Select

    Do While ActiveCell.Row <= 5571
        URL = ActiveCell.Text
TryEnterSite:
        IE.navigate URL

        Do While IE.ReadyState <> 4
            DoEvents
        Loop

        Set html = IE.document

        On Error GoTo TryEnterSite

        NUMERO = html.getElementById("conteudo_meio").getElementsByTagName("tr")(1).getElementsByTagName("td")(1).innerText

        If IsNumeric(NUMERO) Then
            ActiveCell.Offset(0, offsetCount) = Str(NUMERO)
        Else
            ActiveCell.Offset(0, offsetCount) = NUMERO
        End If

        ActiveCell.Offset(1, 0).Select

    Loop

    offsetCount = offsetCount + 1
    Ano = Ano - 1

Loop

问题是在 IE 导航到 URL 的那一行(IE.navigate URL),有时网站就是进不去(内部问题)。因此 html.getelement 找不到元素并给我“找不到元素”并且宏停止。我得到的错误是 RUN-TIME ERROR 91: OBJECT VARIABLE OR WITH BLOCK VARIABLE NOT SET。

我想要的是:当 VBA 找不到对象时,它应该回到 IE.navigate 行。可能吗?我该怎么做呢?我尝试了几天来寻找一些东西,但我没有成功。

【问题讨论】:

  • NUMERO = html.getElementById("conteudo_meio").getElementsByTagName("tr")(1).getElementsByTagName("td")(1).innerText 我不认为这条线实际上是正确的
  • @Jeanno 只要 HTML 元素存在,它就可以工作。

标签: vba internet-explorer error-handling web-scraping


【解决方案1】:

IFERROR 是一个 Excel 函数。您不使用它来处理 VBA 运行时错误。

您需要的是一个On Error GoTo 语句,以及一个类似这样的控制流:

Public Sub Foo()
    On Error GoTo ErrHandler

    Dim result As String

TryGetResult:
    result = GetResult(ActiveCell.Text)

    Exit Sub

ErrHandler:
    'MsgBox Err.Description
    Err.Clear
    Resume TryGetResult
End Sub

GetResult 是一个封装 IE 逻辑的函数——它本身并不是需要的,但是将你的代码分解成做更少事情的更小的函数/过程会更容易维护(并调试!)您的代码。

Resume &lt;label&gt; 指令告诉执行流程返回到该特定标签(标签是一个标识符,后跟一个冒号 - Label:)。 Resume all 本身将返回到引发错误的行,Resume Next 将返回到紧跟引发错误的行。

请注意,您的循环结构似乎为您设置了无限循环,我不确定Sleep 1000 那里的情况 - 您有 5000 多行要通过,并且您在每行之间睡了一整秒.

同样If IsNumeric(NUMERO) = True Then 应该是If IsNumeric(NUMERO) Then - 无需将布尔值与布尔常量进行比较即可为 If 语句创建布尔表达式:布尔值布尔表达式!

您正在使用 ActiveCell - 这有点脆弱:当您的代码在 DoEvents 循环中时,用户可以单击某处,并激活您不希望被激活的单元格,这不会要漂亮。而是使用 WorksheetRange 对象。


这就是我的处理方式 - 假设您的方法称为DoSomething(您没有包含该方法的签名)。当出现错误时,你需要告诉 VBA 跳转到哪里,像这样:

Public Sub DoSomething()

    On Error GoTo ErrHandler

    Dim html As HTMLDocument
    Dim Ano As Long
    Dim offsetCount As Long
    Dim URL As String
    Dim NUMERO As String

    Dim IE As InternetExplorer
    Set IE = New InternetExplorer

    Ano = 2012
    offsetCount = 2

    Do While Ano >= 2005

        Range("E1").Value = Ano
        Range("D2").Select

        Do While ActiveCell.Row <= 5571
            URL = ActiveCell.Text
    TryEnterSite:
            IE.navigate URL

            Do While IE.ReadyState <> 4
                Sleep 1000 'give it a second
                DoEvents
            Loop

            Set html = IE.document

            NUMERO = html.getElementById("conteudo_meio").getElementsByTagName("tr")(1).getElementsByTagName("td")(1).innerText

            If IsNumeric(NUMERO) Then
                ActiveCell.Offset(0, offsetCount) = Str(NUMERO)
            Else
                ActiveCell.Offset(0, offsetCount) = NUMERO
            End If

            ActiveCell.Offset(1, 0).Select

        Loop

        offsetCount = offsetCount + 1
        Ano = Ano - 1

    Loop

    Exit Sub

ErrHandler:
    Err.Clear
    Resume TryEnterSite
End Sub

这是一个开始。现在,当出现 any 错误时,代码将跳转到 TryEnterSite 直到它正常工作 - 如果输入(url)只是坏的,它会一直循环下去,所以你应该有一种方法来验证url 在你之前尝试直到它起作用 - 但这是一个不同的问题。

我上面的意思是,最好将循环体提取到它自己的函数中。你也不应该在ActiveCell 工作。而不是这个:

Range("D2").Select

我会这样做:

Dim xlSheet As Worksheet
Set xlSheet = Sheet1 'or ThisWorkbook.Worksheets("Sheet1")

Dim xlRow As Long
xlRow = 2

Dim xlRange As Range
For xlRow = 2 To 5571

    Set xlRange = xlSheet.Range("D" & xlRow)
    '...

Next

然后你有一个不依赖于当前选择的 range 对象 - 所以不是进行选择并移动该选择,你的循环只是增加一个行号,并且在循环的主体你Set xlRange = xlSheet.Range("D" &amp; xlRow),并使用该对象代替 - 然后用户不能通过简单地单击某处来破坏你的宏,而你的代码在Sleep/DoEvents循环中。


希望对您有所帮助。抱歉,如果这有点过分回答,我更习惯于在Code Review Stack Exchange 上查看工作代码 - 一旦它按预期工作,请随时去那里发布您的代码;那里的社区将帮助您使其更清洁和更好:)

【讨论】:

  • 嗨,垫子!感谢您的回答,但我并没有真正了解如何在我的代码中实现它。我需要它运行,只有当我遇到错误时,它才会再次返回标签 trygetresult。我不知道我会把“退出子”放在哪里。感谢所有其他建议,我已经稍微更改了我的代码!
  • Exit Sub 位于错误处理子例程/标签之前,因此错误处理代码仅在出现错误时运行。在我的回答中,我用封装了 IE 逻辑的 GetResult 函数替换了正文。祝你好运!
  • Mat,请再次检查我的问题,我已经对其进行了编辑,现在完整的代码就在那里。 Tks
  • @LuccasEsperKlotz 感谢您的勾选!我已经编辑了更多细节。
  • 它已经在这里运行了一段时间,我认为它有效!我会发布任何消息!非常感谢你:)
猜你喜欢
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多