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
下找不到我需要的信息。
我确信答案简单得可笑,我就是想不通。有人帮我摆脱痛苦吗?
发生这种情况的原因是,如果node
是一个dt
元素,它与相应的dd
元素之间有一些空白,那么node.NextSibling
是一个全空白文本节点(</dt>
和<dd>
之间的空间)。如果您在调试器中查看它,您将看到node.NextSibling
的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;
}
然后你可以这样使用它:
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::dd
或following-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>