VBA 函数 IFERROR 然后转到第 X 行

标签 vba internet-explorer error-handling 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

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

我想要的是:当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 逻辑的函数 - 它本身并不是需要,但是将代码分解为更小的函数/过程,执行更少的操作将使维护(和调试!)代码变得更加容易.

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

请注意,您的循环构造似乎为您设置了无限循环,我对此不确定 Sleep 1000那里 - 你有 5000 多行要浏览,而你在每一行之间要睡一整秒。

还有If IsNumeric(NUMERO) = True Then应该是If IsNumeric(NUMERO) Then - 无需将 bool 值与 bool 常量进行比较来为 If 语句创建 bool 表达式: bool 值就是 bool 表达式!

您正在使用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

这是一个开始。现在,当出现任何错误时,代码将跳转到 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

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


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

关于VBA 函数 IFERROR 然后转到第 X 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28486600/

相关文章:

循环中的 Python 异常处理

excel - 用于存储和计算唯一 ID 出现次数的多维数组

excel - 在 Visual Basic 中从用户窗体中获取单个变量

php - 某些 JPEG 图像在 IE 中不起作用

javascript - window.attachEvent 在 IE 中调整窗口大小

java - Spring : Default html error page is not properly displayed?

javascript - 如何从函数返回灯箱错误消息

vba - Excel VBA - 在列中搜索特定值并从相邻单元格返回值

excel - 如何在没有填充的情况下更改excel中单元格的值

jquery - 为什么动态添加列表项在 Chrome 和 Firefox 中有效,但在 IE8 中无效?