1

我在 VBA 中创建了一个脚本来从网页中获取特定项目。Year Built我感兴趣的 item () 的值并不总是在同一个索引中,所以在这里使用索引是一个错误的想法。我在下面给出两个链接只是因为项目的价值在两个网页的不同索引中。

站点一

站点二

我最初获得价值的方法是:

.NextSibling.getElementsByTagName("td")(3).innerText

我追求的价值可见为:

在此处输入图像描述

我现在正在尝试的(有效,但位置仍然是假设的,如果位置发生变化会中断):

.NextSibling.LastChild.PreviousSibling.innerText

到目前为止我已经创建了:

Sub GetInformation()
    Dim Http As New XMLHTTP60, links, i&
    Dim Htmldoc As New HTMLDocument, link
    Dim Wb As Workbook, ws As Worksheet, r&

    Set Wb = ThisWorkbook
    Set ws = Wb.Worksheets("Sheet1")

    links = Array( _
        "https://esearch.brazoscad.org/Property/View/114414", _
        "https://esearch.brazoscad.org/Property/View/117608" _
       )

    For Each link In links
        With Http
            .Open "GET", link, False
            .send
            Htmldoc.body.innerHTML = .responseText
        End With


        With Htmldoc.querySelectorAll("tr")
            For i = 0 To .Length - 1
                If InStr(.item(i).innerText, "Year Built") > 0 Then
                    r = r + 1: ws.Cells(r, 1) = .item(i).NextSibling.LastChild.PreviousSibling.innerText
                End If
            Next i
        End With
    Next link
End Sub

如何从网页中获取项目的特定值?

顺便说一句,如果.querySelector()支持:nth-of-type().querySelector("table:nth-of-type(2) tr")当我在不起作用的脚本中使用它时有什么问题。

4

1 回答 1

3

如果 .querySelector() 支持 :nth-of-type(),当我在不起作用的脚本中使用 .querySelector("table:nth-of-type(2) tr") 时有什么问题

Microsoft Internet Controls用于自动化浏览器(IE8+)和创建HTMLDocument时支持它ie.Document。然后,您可以访问极少数的伪类选择器HTMLDocument当通过时innerHTML提供时,情况并非如此MSXML2.XMLHTTP。请记住,您输入HTMLDocument变量的内容.innerHTML在 XHR 中会有所不同,其中 javascript 不会运行与IEjs 将运行并且浏览器将修改内容/请求其他文件,从而为您留下修改后的.document. 如开头所述,后者当然还有浏览器/文档模式依赖项。

选择器table:nth-of-type(2) tr,即使支持,在这里也不合适。

我感兴趣的项目(Year Built)的值并不总是在同一个索引中,所以在这里使用索引是一个错误的想法

根据对代码的仔细检查,您试图考虑的可变性似乎是目标表中列数的潜在差异,因此您的元素可能驻留在td给定行内的不同索引处(例如,您没有尝试考虑行可变性...)。所以我们总体上正在寻找一种不同的关系;不需要元素之间的关系;或动态确定合适的索引;或者甚至是这些的组合。

国际海事组织的考虑是:

  • 相同的 URI,但页面上的替代元素具有更短,希望更健壮的选择器;
  • 不同的 XHR URI,其中所需元素与更健壮的选择器相关联,例如唯一 id;
  • 一个script带有漂亮正则表达式可抓取字符串的标签 (var yearBuilt = 1234;);
  • 一种具有较少依赖性和/或根据经验具有较高稳定性概率的定位策略

此外,

  • 为更快的检索而优化

我承认以上是对相同整体想法的重新散列。

查看考虑因素和提供的两个链接:

与 相关联的构建年份MAIN AREA仅出现在文档中的一个位置。注意:我保留这样的假设,即这是相应标题行的下一行。我没有检查足够多的链接来了解今年的价值是否会因房产面积而异,而且您没有说明哪个是必需的。MAIN AREA在此示例中,显示为列出构建日期的第一部分。

该页面似乎没有从其他请求中检索所需的内容,因此替代来源不是很明显。似乎没有专用的公共 API。搜索功能不提供来自其 POST 请求的必要信息,并且可下载文件有 3-4 个月的延迟,主要是 .txt,并且不提供任何实际机会来更快地识别所需信息(实际上将是更多的工作和更少的可靠性)。

这留下了考虑 4。您需要一种方法来定位右表中的右列。html 具有非常重复的结构,几乎没有漂亮的“钩子”。而不是根据表的关系生成更脆弱的路径,您明智地选择了trs 上的循环(ergo 应该在表中)在tr innerText. 因此,权衡了标题字符串出现在不同列和/或不同表中的风险,以换取较短的遍历路径和移动到假定包含感兴趣数据的下一行的灵活性。

到目前为止,我认为是不错的选择,尽管我个人会选择将搜索限制在标题 ( th) 中,然后升级到父级。这里的额外好处是我可以为你的下一部分减轻压力:

.Item(i).NextSibling.LastChild.PreviousSibling.innerText

在这里,您建立了一个不必要的假设/风险,即您感兴趣的列将始终是倒数第二个。虽然您可以循环所有标题并转到父节点,但我会冒险通过在面板标题中搜索唯一字符串来限制到适当的表,然后next-sibling在检查标题之前抓取该表。它为 IMO 引入了关于内容和内容关系panel heading的合理假设。然后,这使我们能够根据 为标题找到正确的索引,并使用该索引来索引下一行的 。这减轻了位置不是倒数第二的情况。然后,您可以寻找一些进一步的优化。我将匹配项设置为变量以便更快地引用。tablepaneltabletds

尽管有两个循环结构,但更多的代码行但没有更大的复杂性,在正确元素上匹配的安全性更高,合适的退出策略和更少的循环(由于表的目标)。

总体而言,您的策略是一个不错的策略。我个人会冒着尝试获得正确表格的风险,而不是假设正确的列是倒数第二列。我采用了稍微不同的关系并动态确定了正确的索引。我对解决方案并不完全满意,但感觉足够好。


VBA:

Option Explicit

Public Sub GetInformation()
    Dim Http As New XMLHTTP60, links, i&
    Dim htmlDoc As New HTMLDocument, link
    Dim Wb As Workbook, ws As Worksheet, r&

    Set Wb = ThisWorkbook
    Set ws = Wb.Worksheets("Sheet1")

    links = Array( _
            "https://esearch.brazoscad.org/Property/View/114414", _
            "https://esearch.brazoscad.org/Property/View/117608" _
            )

    For Each link In links
        With Http
            .Open "GET", link, False
            .send
            htmlDoc.body.innerHTML = .responseText
        End With

        Dim panels As Object, table As Object, headers As Object

        Set panels = htmlDoc.querySelectorAll(".panel-heading")

        For i = 0 To panels.Length - 1
            If InStr(panels.Item(i).innerText, "Property Improvement - Building") > 0 Then
                Set table = panels.Item(i).NextSibling 'assumption on relationship
                Exit For
            End If
        Next i

        If Not table Is Nothing Then

            Set headers = table.getElementsByTagName("th")

            For i = 0 To headers.Length - 1
                If InStr(headers(i).innerText, "Year Built") > 0 Then
                    r = r + 1: ws.Cells(r, 1) = headers(i).ParentNode.NextSibling.Children(i).innerText
                    Exit For
                End If
            Next
        End If
        Set htmlDoc = Nothing: Set table = Nothing
    Next link
End Sub

参考资料(VBE>工具>参考资料):

  1. Microsoft HTML 对象库
  2. Microsoft XML v(n) '你的版本
于 2019-11-09T12:34:39.547 回答