HTML 敏捷包 - 删除不需要的标签而不删除内容?
Posted
技术标签:
【中文标题】HTML 敏捷包 - 删除不需要的标签而不删除内容?【英文标题】:HTML agility pack - removing unwanted tags without removing content? 【发布时间】:2012-09-29 00:59:58 【问题描述】:我在这里看到了一些相关的问题,但它们并没有完全谈论我面临的同一个问题。
我想使用 html Agility Pack 从我的 HTML 中删除不需要的标签,而不会丢失标签中的内容。
例如,在我的场景中,我想保留标签“b
”、“i
”和“u
”。
对于像这样的输入:
<p>my paragraph <div>and my <b>div</b></div> are <i>italic</i> and <b>bold</b></p>
生成的 HTML 应该是:
my paragraph and my <b>div</b> are <i>italic</i> and <b>bold</b>
我尝试使用HtmlNode
的Remove
方法,但它也删除了我的内容。有什么建议吗?
【问题讨论】:
你试过HtmlNode.RemoveChild(HtmlNode oldChild, bool keepGrandChildren)
的方法吗?
检查***.com/questions/18153998/…
@bidou 如果必须强制执行安全性,我非常怀疑使用正则表达式是个好主意。
【参考方案1】:
我根据 Oded 的建议编写了一个算法。这里是。像魅力一样工作。
它会删除除strong
、em
、u
和原始文本节点之外的所有标签。
internal static string RemoveUnwantedTags(string data)
if(string.IsNullOrEmpty(data)) return string.Empty;
var document = new HtmlDocument();
document.LoadHtml(data);
var acceptableTags = new String[] "strong", "em", "u";
var nodes = new Queue<HtmlNode>(document.DocumentNode.SelectNodes("./*|./text()"));
while(nodes.Count > 0)
var node = nodes.Dequeue();
var parentNode = node.ParentNode;
if(!acceptableTags.Contains(node.Name) && node.Name != "#text")
var childNodes = node.SelectNodes("./*|./text()");
if (childNodes != null)
foreach (var child in childNodes)
nodes.Enqueue(child);
parentNode.InsertBefore(child, node);
parentNode.RemoveChild(node);
return document.DocumentNode.InnerHtml;
【讨论】:
感谢您的解决方案!但请注意,它不会删除嵌套在可接受标签内的不可接受标签——例如,如果您允许“P”但不允许“SPAN”,则不会删除跨度。我通过递归运行此方法来解决此问题,从“var 节点...”拆分为一个单独的方法,该方法在可接受列表中的任何“节点”var 上调用自身并具有子节点。 伟大的@SeanMahan。随意使用您的解决方案编辑我的帖子 - 我相信它会更好:) @SeanMahan 请做!我想看看你的解决方案。 不适用于输入:'10 > 7 但 10 7 but 10' 但这也是非常无效的 HTML。【参考方案2】:如何以递归方式从 html 字符串中删除给定的不需要的 html 标记列表
我接受了@mathias 的回答并改进了他的扩展方法,以便您可以提供要排除的标签列表作为List<string>
(例如"a","p","hr"
)。我还修复了逻辑,使其递归地正常工作:
public static string RemoveUnwantedHtmlTags(this string html, List<string> unwantedTags)
if (String.IsNullOrEmpty(html))
return html;
var document = new HtmlDocument();
document.LoadHtml(html);
HtmlNodeCollection tryGetNodes = document.DocumentNode.SelectNodes("./*|./text()");
if (tryGetNodes == null || !tryGetNodes.Any())
return html;
var nodes = new Queue<HtmlNode>(tryGetNodes);
while (nodes.Count > 0)
var node = nodes.Dequeue();
var parentNode = node.ParentNode;
var childNodes = node.SelectNodes("./*|./text()");
if (childNodes != null)
foreach (var child in childNodes)
nodes.Enqueue(child);
if (unwantedTags.Any(tag => tag == node.Name))
if (childNodes != null)
foreach (var child in childNodes)
parentNode.InsertBefore(child, node);
parentNode.RemoveChild(node);
return document.DocumentNode.InnerHtml;
【讨论】:
注意:如果我没看错,这个函数会删除不需要的标签列表,@mathias 发布的函数会保留可接受的标签。这是完全不同的。 @StarQuake 这是对原始问题的回答,即删除不需要的标签。我刚刚修改了 Mathias 的代码,使其更加灵活。 原始问题询问如何通过提供 allowedTags 来删除不需要的标签。原始问题的标题具有误导性。给出的示例还说明了一个保留 allowedTags 的函数,而不是删除不允许的标签。 @StarQuake 这个问题没有提到任何关于需要“allowedTags”列表的实现,它只是给出了一个要在一个实例中保留的标签的例子。 Mathias 的回答通过硬编码的白名单方法给出了解决方案。我的回答通过可变黑名单方法提供了一个解决方案,以增加灵活性。既没有对错,也没有对错,只是原始问题的替代解决方案。【参考方案3】:如果您不想使用 Html 敏捷包,但仍想删除不需要的 Html 标签,则可以执行以下操作。
public static string RemoveHtmlTags(string strHtml)
string strText = Regex.Replace(strHtml, "<(.|\n)*?>", String.Empty);
strText = HttpUtility.HtmlDecode(strText);
strText = Regex.Replace(strText, @"\s+", " ");
return strText;
【讨论】:
请勿使用正则表达式解析或分析 HTML。【参考方案4】:尝试以下方法,您可能会发现它比其他建议的解决方案更简洁:
public static int RemoveNodesButKeepChildren(this HtmlNode rootNode, string xPath)
HtmlNodeCollection nodes = rootNode.SelectNodes(xPath);
if (nodes == null)
return 0;
foreach (HtmlNode node in nodes)
node.RemoveButKeepChildren();
return nodes.Count;
public static void RemoveButKeepChildren(this HtmlNode node)
foreach (HtmlNode child in node.ChildNodes)
node.ParentNode.InsertBefore(child, node);
node.Remove();
public static bool TestYourSpecificExample()
string html = "<p>my paragraph <div>and my <b>div</b></div> are <i>italic</i> and <b>bold</b></p>";
HtmlDocument document = new HtmlDocument();
document.LoadHtml(html);
document.DocumentNode.RemoveNodesButKeepChildren("//div");
document.DocumentNode.RemoveNodesButKeepChildren("//p");
return document.DocumentNode.InnerHtml == "my paragraph and my <b>div</b> are <i>italic</i> and <b>bold</b>";
【讨论】:
【参考方案5】:在删除节点之前,获取其父节点及其InnerText
,然后删除节点并将InnerText
重新分配给父节点。
var parent = node.ParentNode;
var innerText = parent.InnerText;
node.Remove();
parent.AppendChild(doc.CreateTextNode(innerText));
【讨论】:
InnerText 破坏了层次结构。想象更多深度,例如:<p>my <b>paragraph <div>and my <b><span><i>div</i></span></b></div> are <i>italic</i> and <b>bold</b></b></p>
。 InnerText 搞砸了 HTML 并将其转换为文本,但这并不是我在所有情况下都想要的。我仍然想要允许标签的 HTML。
@MathiasLykkegaardLorenzen - 我的意思是您只使用 unwanted 元素。
是的。但是,如果您在这里查看这些数据会怎样。 lalala <span>omg <b>this</b> is bold</span>
。首先,您会发现 span
元素。这是不允许的,所以你用它的InnerText
值交换它。但是,这会破坏我仍然需要的内部 b
标签。我希望这能更好地解释它。
@MathiasLykkegaardLorenzen - 当然,您可以简单地执行 same 但使用 node 的 InnerHtml
而不是父节点(也就是说,采取节点的InnerHtml
并在删除节点后替换父InnerHtml
)。您也可以使用节点的InnerHtml
递归地执行此操作。
谢谢 - 我会尝试并回复你。以上是关于HTML 敏捷包 - 删除不需要的标签而不删除内容?的主要内容,如果未能解决你的问题,请参考以下文章