VBA 函数 IFERROR 然后转到第 X 行

Luc*_*otz 2 error-handling internet-explorer vba web-scraping

我在使用 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
Run Code Online (Sandbox Code Playgroud)

问题是,在 IE 导航到 URL (IE.navigate URL) 的行中,有时网站无法进入(内部问题)。因此 html.getelement 找不到该元素并给我“找不到元素”并且宏停止。我得到的错误是运行时错误 91:对象变量或块变量未设置。

我想要的是:当VBA找不到对象时,它应该返回到IE.navigate行。是否可以?我该怎么做呢?几天来我一直在努力寻找一些东西,但没有成功。

Mat*_*don 5

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
Run Code Online (Sandbox Code Playgroud)

GetResult封装 IE 逻辑的函数在哪里- 它本身并不需要,但是将代码分解为执行更少操作的较小函数/过程将使维护(和调试!)代码变得更加容易。

Resume <label>指令告诉执行流程返回到该特定标签(标签是一个标识符,后跟一个冒号 - Label:)。Resumeall 本身将返回到引发错误的行,并将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
Run Code Online (Sandbox Code Playgroud)

这是一个开始。现在,当出现任何错误时,代码将跳转到TryEnterSite它起作用为止 - 如果输入(url)很糟糕,它将永远循环,因此您应该有一种方法在尝试之前验证 url 直到它起作用为止- 但是那是另一个问题了。

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

Range("D2").Select
Run Code Online (Sandbox Code Playgroud)

我会这样做:

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
Run Code Online (Sandbox Code Playgroud)

然后你有一个不依赖于当前选择的范围对象 - 因此,你的循环只需增加一个行号,而不是进行选择并移动该选择,然后在循环体中Set xlRange = xlSheet.Range("D" & xlRow)使用该对象- 然后当您的代码处于Sleep/循环中时,用户无法通过简单地单击某处来破坏您的宏DoEvents


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