我正在使用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
下找到我需要的信息。
我确信答案非常简单,我只是无法弄明白。有人让我摆脱了苦难吗?
答案 0 :(得分:8)
发生这种情况的原因是,如果node
是dt
元素,并且通过某个空格与其对应的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>