VBA 函数 IFERROR THEN GO TO LINE X

VBA function IFERROR THEN GO TO LINE X

我在使用 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 找不到元素并给我 "element not found" 并且宏停止。我得到的错误是 运行-TIME ERROR 91: OBJECT VARIABLE OR WITH BLOCK VARIABLE NOT SET.

我要的是:当VBA没有找到对象时,应该回到IE.navigate行。可能吗?我该怎么做呢?我想了好几天都在找东西,但我没有成功。

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 逻辑的函数 - 它本身 不需要 ,但将您的代码分解成更小的 functions/procedures更少的东西将使维护(和调试!)您的代码变得更加容易。

Resume <label> 指令告诉执行流程 return 到那个特定的标签(标签是一个标识符后跟一个冒号 - Label:)。 Resume 本身将 return 到引发错误的行,而 Resume Next 将 return 到紧跟在引发错误的行之后的行。

请注意,您的循环结构似乎为无限循环做好了准备,我不确定 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" & xlRow),然后使用该对象代替 - 然后当你的代码在 Sleep/[=25 中时,用户不能通过简单地点击某个地方来破坏你的宏=]循环。


希望对您有所帮助。抱歉,如果这个答案有点过分,我更习惯于在 Code Review Stack Exchange 上查看工作代码 - 一旦代码按预期工作,请随时去那里 post;那里的社区会帮助你让它变得更干净、更好:)