html - 尝试检查 DispStaticNodeList 时 Excel 崩溃

标签 html excel vba dom web-scraping

情况:

我正在尝试检查变量 a,在本地窗口中显示为 DispStaticNodeList 对象;每次我尝试执行此操作时,Excel 都会崩溃。

这是本地窗口中的变量 a,显然是 DispStaticNodeList 类型:

Locals window

重现 Excel 崩溃:

  1. 尝试在本地窗口中展开项目会导致 Excel 崩溃。
  2. 尝试使用 For Each 循环它也会导致崩溃。*TestFail

研究亮点:

  1. 我做了一些挖掘,搜索诸如 Excel + Crash + DispStaticNodeList 之类的组合,结果为零;至少我使用的谷歌搜索词是这样。很确定我的 Google-Fu 很弱。
  2. 如果我相信这个article我正在处理 MSHTML 支持的 COM 对象。

  3. 并根据this :

If the name is DispStaticNodeList, we can be pretty sure it's an array..(or at least has array semantics).

基于第 3 点,我编写了下面的代码 TestPass,它确实成功地循环了它,但我不完全理解为什么。我已经设置了一个对象,然后循环了它的 len!

  1. 我刚找到this其中指出:

NodeList objects are collections of nodes such as those returned by properties such as Node.childNodes and the document.querySelectorAll() method.

所以看起来这个对象可能是一个 NodeList,它给出了即时窗口中的描述似乎是正确的,并且作为一个列表我可以遍历它的长度,但不确定为什么 For Each 不起作用以及 Excel 崩溃的原因。同事建议它可能由于数据的分层性质而崩溃。我还注意到有一些名为 IDOMNodeIteratorNodeIterator 的类,但我不确定是否可以按照 NodeList 的描述使用它们方法 here .

问题:

什么是 a?为什么它会在尝试使用 For Each 检查或循环时导致 Excel 崩溃?

成功循环的代码:

Option Explicit

Public Sub TestPass()
    Dim html As HTMLDocument
    Set html = GetTestHTML
    Dim a As Object, b As Object

    Set a = html.querySelectorAll("div.intro p")

    Dim i As Long

    For i = 0 To Len(a) -1
        On Error Resume Next
        Debug.Print a(i).innerText    '<== HTMLParaElement
        On Error GoTo 0
    Next i
End Sub

Public Function GetTestHTML(Optional ByVal url As String = "https://www.w3schools.com/cssref/trysel.asp") As HTMLDocument
    Dim http As New XMLHTTP60
    Dim html As New HTMLDocument
    With http                                    'Set http = CreateObject("MSXML2.XMLHttp60")
        .Open "GET", url, False
        .send
        html.body.innerHTML = .responseText
        Set GetTestHTML = html
    End With
End Function

*测试失败 导致崩溃的代码:

Public Sub TestFail()
    Dim html As HTMLDocument
    Set html = GetTestHTML
    Dim a As Object, b As Object

    Set a = html.querySelectorAll("div.intro p")

    For Each b In a

    Next b
End Sub

注意事项:

我向一位同事发送了一份测试工作簿,他也能够通过给定的示例重现此行为。

项目引用:

Project references

HTML 示例(还提供了链接)

<div class="noSel">
<h1 style=""><span class="markup">&lt;h1&gt;</span>Welcome to My Homepage<span class="markup">&lt;/h1&gt;</span></h1>

<div id="helpIntro" style="">
<span class="markup">&lt;div class="intro"&gt;</span>
<div class="intro">
<p style="margin-top: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p&gt;</span>My name is Donald <span id="Lastname" style=""><span class="markup">&lt;span id="Lastname"&gt;</span>Duck.<span class="markup">&lt;/span&gt;</span></span><span class="markup">&lt;/p&gt;</span></p>
<p id="my-Address" style="border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p id="my-Address"&gt;</span>I live in Duckburg<span class="markup">&lt;/p&gt;</span></p>
<p style="margin-bottom: 4px; border-color: rgb(255, 102, 102); background-color: rgb(255, 255, 153);"><span class="markup">&lt;p&gt;</span>I have many friends:<span class="markup">&lt;/p&gt;</span></p>
</div>
<span class="markup">&lt;/div&gt;</span>
</div>

<br>
<div class="helpUl">
<span class="markup">&lt;ul id="Listfriends&gt;</span>
<ul id="Listfriends" style="margin-top:0px;margin-bottom:0px;">

<li><span class="markup">&lt;li&gt;</span>Goofy<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Mickey<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Daisy<span class="markup">&lt;/li&gt;</span></li>
<li><span class="markup">&lt;li&gt;</span>Pluto<span class="markup">&lt;/li&gt;</span></li>
</ul>       
<span class="markup">&lt;/ul&gt;</span>
</div>

<ul style="display:none;"></ul>
<p style=""><span class="markup">&lt;p&gt;</span>All my friends are great!<span class="markup">&lt;br&gt;</span><br>But I really like Daisy!!<span class="markup">&lt;/p&gt;</span></p>

<p lang="it" title="Hello beautiful" style=""><span class="markup">&lt;p lang="it" title="Hello beautiful"&gt;</span>Ciao bella<span class="markup">&lt;/p&gt;</span></p>

编辑:我也能够以下列方式循环:

Public Sub Test()
    Dim html As MSHTML.HTMLDocument, i As Long
    Set html = GetTestHTML

    For i = 0 To html.querySelectorAll("div.intro p").Length - 1
       Debug.Print html.querySelectorAll("div.intro p")(i).innerText
    Next i

End Sub

最佳答案

If the name is DispStaticNodeList, we can be pretty sure it's an array..(or at least has array semantics).

数组可以通常用For Each 循环迭代,但是it's more efficient使用 For 循环迭代它们。看起来你得到的不是完全数组,虽然它似乎支持索引,但显然不支持枚举,这可以解释爆炸当您尝试使用 For Each 循环枚举它时。

看起来 locals 工具窗口可能正在使用 For Each 语义来列出集合中的项目。

我不熟悉那个特定的库,所以这是一些(受过教育的)猜测,但是制作一个不能用 For Each 迭代的自定义 COM 集合类型非常容易> 在 VBA 中循环 - 通常错误是在 VBA 端捕获的......似乎库的枚举器实现中可能存在错误(假设它有一个枚举器)导致它抛出一个异常,最终未处理并以某种方式需要一切都结束了...问题是,您无法修复并重新编译该库...所以您唯一能做的就是避免使用 For Each 循环迭代该类型,并避免在 locals 工具窗口中展开它(因此,...经常保存您的工作!)。

This article从 C#/.NET 的角度给出了 COM 枚举如何工作的好主意。当然,该库不是托管代码 (.NET),但所使用的 COM 概念是相同的。

TL;DR:并不是因为您可以 For...Next 才可以 For Each;涉及的 COM 类型必须明确支持枚举。如果 VBA 代码使用 For Each 循环编译,那么它确实如此,因此它一定是类型枚举器中的错误。

关于html - 尝试检查 DispStaticNodeList 时 Excel 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50021133/

相关文章:

javascript - AJAX 回调中 anchor 标记的奇怪解析

c# - 是否可以取消选择 RadioButton 组中的所有值?

html - Flexslider 控制导航在 Safari 中显示低于正常的 CSS 指令

excel - 使用类的文本名称创建一个新对象

excel - 无效或不合格的引用

excel - 以编程方式更改工作簿中的链接

html - "Billing address same as shipping address"jQuery 脚本 : is there a better way to do this?

c - 如何使用 VBA 在 Excel 16 for Mac 中打印 C BSTR?

excel - 如何根据单元格值连接excel VBA中的列和行标题

VBA:MSXML2.DOMDocument 在宏中更改为 MSXML2.DOMDocument40