我有以下问题。
我遇到的html格式不正确,在这种情况下我使用html agility pack选择节点时遇到问题。
代码如下:
string strHtml = @"
<html>
<div>
<p><strong>Elem_A</strong>String_A1_2 String_A1_2</p>
<p><strong>Elem_B</strong>String_B1_2 String_B1_2</p>
</div>
<div>
<p><strong>Elem_A</strong>String_A2_2 <String_A2_2> asdas</p>
<p><strong>Elem_B</strong>String_B2_2 String_B2_2</p>
</div>
</html>";
HtmlAgilityPack.HtmlDocument objHtmlDocument = new HtmlAgilityPack.HtmlDocument();
objHtmlDocument.LoadHtml(strHtml);
HtmlAgilityPack.HtmlNodeCollection colnodePs = objHtmlDocument.DocumentNode.SelectNodes("//p");
List<string> lststrText = new List<string>();
foreach (HtmlAgilityPack.HtmlNode nodeP in colnodePs)
{
lststrText.Add(nodeP.InnerHtml);
}
问题是String_A2_2括在括号中。
所以htmlagility pack在lststrText中返回5个字符串而不是4个字符串。
那么有可能让htmlagility pack将元素3返回为"<strong>Elem_A</strong>String_A2_2 <String_A2_2> asdas"
吗?
或者我可以做一些预处理来关闭元素?
lststrText的当前内容是
lststrText[0] = "<strong>Elem_A</strong>String_A1_2 String_A1_2"
lststrText[1] = "<strong>Elem_B</strong>String_B1_2 String_B1_2"
lststrText[2] = ""
lststrText[3] = ""
lststrText[4] = "<strong>Elem_B</strong>String_B2_2 String_B2_2"
大多数html解析器尝试构建一个工作DOM,这意味着不接受悬空标签。它们将被转换或以某种方式关闭。
如果只选择节点对您来说很重要,速度和大量数据不是问题,您可以使用正则表达式获取所有<p>标签:
Regex reMatchP = new Regex(@"<(p)>.*?</\1>");
foreach (Match m in reMatchP.Matches(strHtml))
{
Console.WriteLine(m.Value);
}
此正则表达式假定<p>标记格式良好且已关闭。
如果您要在程序中运行此正则表达式,则应将其声明为:
static Regex reMatchP = new Regex(@"<(p)>.*?</\1>", RegexOptions.Compiled);
[编辑:敏捷包更改]
如果要使用HtmlAgility包,可以修改HtmlDocument.cs中的PushNodeEnd函数:
if (HtmlNode.IsCDataElement(CurrentNodeName()))
{
_state = ParseState.PcData;
return true;
}
// new code start
if ( !AllowedTags.Contains(_currentnode.Name) )
{
close = true;
}
// new code end
其中AllowedTags将是所有已知标签的列表:b,p,br,span,div等。
输出不是你想要的100%,但可能足够接近?
<strong>Elem_A</strong>String_A1_2 String_A1_2
<strong>Elem_B</strong>String_B1_2 String_B1_2
<strong>Elem_A</strong>String_A2_2 <ignorestring_a2_2></ignorestring_a2_2> asdas
<strong>Elem_B</strong>String_B2_2 String_B2_2