HtmlAgilityPack NextSibling.InnerText值为空

时间:2014-08-27 19:17:27

标签: c# xpath html-agility-pack siblings

我正在使用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;
    }

这适用于电话,但出于某种原因,当电子邮件:&#34;节点出现,NextSibling.InnerHTML&amp; NextSibling.InnerText是空白的,尽管下一个兄弟肯定有数据。如果我实际转到node中的details并查看它,则InnerHTML是整个格式化链接,InnerText是电子邮件地址。

NextSibling.InnerText无法正常工作,因为A标签使其成为儿童或其他什么东西?我查看了调试器,但无法在NextSibling下找到我需要的信息。

我确信答案非常简单,我只是无法弄明白。有人让我摆脱了苦难吗?

2 个答案:

答案 0 :(得分:8)

发生这种情况的原因是,如果nodedt元素,并且通过某个空格与其对应的dd元素分开,则node.NextSibling是全部 - 空白文本节点(</dt><dd>之间的空格)。如果您在调试器中查看它,您会看到node.NextSibling的{​​{1}}是NodeType而不是HtmlNodeType.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;
}

以上是我上述方法中使用的有点棘手的XPath的细分:

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

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

(a) following-sibling::*

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

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

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

(c) following-sibling::*[1][self::dd] 选择集合(c)中的第一个节点,该节点应始终为1或0个节点。

您很可能只使用SelectSingleNode()following-sibling::dd,但上述路径包含保护措施。例如,如果由于某种原因,您拥有以下XML并且您当前的节点是following-sibling::*元素:

Tel:

<dl> <dt>Tel:</dt> <dt>Address:</dt> <dd>50 Fake St.</dd> </dl> 会给你结果“50 Fake St.”,而following-sibling::dd会给你结果“地址:”。相反,在这种情况下,following-sibling::*会选择一个空节点集,因此该方法会正确生成一个空字符串作为结果。

答案 1 :(得分:0)

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>