尝试检查DispStaticNodeList时Excel崩溃

时间:2018-04-25 11:28:03

标签: html5 excel-vba dom web-scraping crash

情况:

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

以下是本地窗口中变量a,显然属于DispStaticNodeList类型:

Locals window

重现Excel崩溃:

  1. 尝试在“本地窗口”中展开项目会导致Excel崩溃。
  2. 尝试使用For Each循环播放它也会导致崩溃。* TestFail
  3. 研究亮点:

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

    3. 根据this

    4.   

      如果名称是DispStaticNodeList,我们可以肯定它是一个   array ..(或者至少有数组语义)。

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

      1. 我刚刚发现this声明:
      2.   

        NodeList对象是节点的集合,例如返回的节点   Node.childNodes和document.querySelectorAll()等属性   方法

        所以看起来这个对象可能是NodeList,它给出了即时窗口中的描述似乎是正确的,作为一个列表,我可以遍历它的长度,但不确定为什么For Each赢了& #39;工作以及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
        

        * TestFail 导致崩溃的代码:

        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示例(还提供了链接)

        &#13;
        &#13;
        <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>
        &#13;
        &#13;
        &#13;

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

        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
        

1 个答案:

答案 0 :(得分:2)

  

如果名称是DispStaticNodeList,我们可以肯定它是一个数组..(或者至少具有数组语义)。

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

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

我不熟悉那个特定的库,所以这是一些(有教养的)猜测,但是很容易制作一个不能用a迭代的自定义COM集合类型。 VBA中的For Each循环 - 通常错误是在VBA端捕获的......似乎库的枚举器实现中可能存在错误(假设它有一个枚举器)它抛出一个异常,最终得不到处理,并以某种方式将所有东西都拿下来...事情是,你无法修复&amp;重新编译该库...所以你唯一能做的就是避免使用For Each循环迭代该类型,并避免在 locals 工具窗口中扩展它(等等,...经常保存你的工作!)。

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

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