HtmlAgilityPack NextSibling.InnerText value is blank

本文关键字:is blank value InnerText NextSibling HtmlAgilityPack | 更新日期: 2023-09-27 18:19:42

我正在使用HtmlAgilityPack抓取一些数据。

HTML如下所示:

<div id="id-here">
  <dl>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
  </dl>
</div>

现在我遇到的问题是,并不总是有一定数量的字段,所以我不能可靠地访问它们中的每一个,比如:

//*[@id="id-here"]/dl[1]/dd[1]

由于dd[1]可能是一个页面上的名称,而另一个页面中的电话用户未能填写名称,因此字段被隐藏。

所以我获取了所有的DT和DD节点,如下所示:

//*[@id="id-here"]/dl[1]/dt | //*[@id="id-here"]/dl[1]/dd

现在我检查每个节点,看看它是否与我想要的字段匹配,并取NextSibling值,如下所示:

    foreach (HtmlNode node in details)
    {
        if (node.InnerText.Contains("Tel:")) telephone = node.NextSibling.InnerText;
        if (node.InnerText.Contains("Email:")) email = node.NextSibling.InnerText;
    }

这对电话来说很好,但由于某种原因,当"电子邮件:"节点出现时,NextSibling.InnerHTML&NextSibling.InnerText为空,尽管下一个同级肯定具有数据。如果我真的转到details中的node并查看它,InnerHTML是整个格式化的链接,InnerText是电子邮件地址。

NextSibling.InnerText不工作是因为A标签使它成为子标签还是其他什么?我在调试器中查看了一下,但在NextSibling下找不到我需要的信息。

我确信答案简单得可笑,我就是想不通。有人帮我摆脱痛苦吗?

HtmlAgilityPack NextSibling.InnerText value is blank

发生这种情况的原因是,如果node是一个dt元素,它与相应的dd元素之间有一些空白,那么node.NextSibling是一个全空白文本节点(</dt><dd>之间的空间)。如果您在调试器中查看它,您将看到node.NextSiblingNodeTypeHtmlNodeType.Text,而不是HtmlNodeType.Element

我建议创建一种方便的方法来获取dt节点对应的dd:的文本

internal static string GetMatchingDdValue(HtmlNode dtNode)
{
    var found = dtNode.SelectSingleNode("following-sibling::*[1][self::dd]");
    return found == null ? "" : found.InnerText;
}

然后你可以这样使用它:

if (node.InnerText.Contains("Tel:")) { telephone = GetMatchingDdValue(node); }

下面是我上面的方法中使用的有点棘手的XPath的分解:

(a) following-sibling::*

^选择共享相同的所有元素父节点作为当前节点并在其之后发生。

(b) following-sibling::*[1]

^选择集合(a)中的第一个节点(如果有)

(c) following-sibling::*[1][self::dd] 

^选择集合(b)中的所有节点是名为"dd"的元素

SelectSingleNode()选择集合(c)中的第一个节点,该节点应始终为1或0个节点。

您很可能只使用following-sibling::ddfollowing-sibling::*,但上述路径包含保护措施。例如,如果出于某种原因,您有以下XML,而当前节点是Tel:元素:

<dl>
  <dt>Tel:</dt>
  <dt>Address:</dt>
  <dd>50 Fake St.</dd>
</dl>

following-sibling::dd会给你"50 Fake St."的结果,而following-sibling::*会给你结果"Address:"。相反,在这种情况下,following-sibling::*[1][self::dd]将选择一个空节点集,因此该方法将正确地生成一个空字符串作为结果。

var html = @"
<div id='id-here'>
  <dl>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
    <dt> Field Name </dt>
    <dd> Value for above field name </dd>
  </dl>
</div>";
html = new Regex(">'r'n''s*<").Replace(html,"><");
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);
Console.Write(doc.DocumentNode.SelectNodes("//dt")[0].NextSibling.OuterHtml);
<dd> Value for above field name </dd>