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
循环中时,用户可以单击某处,并激活您不希望被激活的单元格,那不会很漂亮。相反,使用 Worksheet
和 Range
对象。
这是我的处理方式 - 假设您的方法名为 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;那里的社区会帮助你让它变得更干净、更好:)
我在使用 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
循环中时,用户可以单击某处,并激活您不希望被激活的单元格,那不会很漂亮。相反,使用 Worksheet
和 Range
对象。
这是我的处理方式 - 假设您的方法名为 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;那里的社区会帮助你让它变得更干净、更好:)