我该如何在ASP.NET中从字符串中删除HTML标签?

129
使用 ASP.NET,如何可靠地从给定的字符串中剥离 HTML 标记(不使用正则表达式)?我正在寻找类似于 PHP 的strip_tags的东西。
示例: <ul><li>Hello</li></ul> 输出:
"Hello"
我试图不重复造轮子,但目前还没有找到符合我的需求的解决方案。

我想PHP strip_tags在幕后使用正则表达式! - stevehipwell
10
@Daniel:因为正则表达式在这方面做得很差,特别是如果你有嵌套的情况。 - Joel Coehoorn
嗯,从官方注释和评论来看,PHP的strip_tags似乎也不是特别可靠。http://uk.php.net/strip_tags - Zhaph - Ben Duguid
这个回答解决了你的问题吗?如何在不知道字符串中包含哪些标签的情况下删除所有HTML标记? - Michael Freidgeim
14个回答

115

如果只是从一个字符串中剥离所有HTML标签,使用正则表达式也可以可靠地实现。将以下内容替换:

<[^>]*(>|$)

使用空字符串进行全局替换。不要忘记在此之后规范化字符串,替换为:

[\s\r\n]+

用单个空格分隔,并修剪结果。可选地,将任何HTML字符实体替换回实际字符。

注意

  1. 有一个限制:HTML和XML允许在属性值中使用>。当遇到这样的值时,此解决方案将返回损坏的标记。
  2. 从技术上讲,此解决方案是安全的:结果永远不会包含任何可用于进行跨站点脚本或破坏页面布局的内容。只是不太清洁罢了。
  3. 与所有HTML和正则表达式一样:
    如果必须在所有情况下都正确,请使用适当的解析器

55
虽然没有要求,但我认为许多读者也希望去除HTM编码,例如 &quote;。 我会将其与 WebUtility.HtmlDecode 结合使用(它本身不会删除标签)。请在删除标签后使用它,因为它可能会重写 &gt;&lt;。例如:WebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty)) - Yahoo Serious
@YahooSerious 感谢您提供的示例。这非常有效。谢谢。 - SearchForKnowledge
Html Agility Pack是一个不错的选择,我早在WebForms时代就用它来剥离整个网页以使用内容! - Bojangles
我是一个编程新手,不知道如何将上述webUtility.HtmlDecode代码实现到我的源代码中。我正在使用CkEditor。 - Lemdor
3
@YahooSerious 这会允许一个XSS向量输入,但是 > script < alert("XXS"); > / script < 不会被正则表达式清理,而是通过HtmlDecode转换为<script>alert("XXS");</script> - user70568
1
@Heather 很好的观点。实体解码后,必须再次进行HTML标签剥离。 - Tomalak

78

现在就去下载HTMLAgilityPack吧! ;) 下载链接

它可以使你加载和解析HTML。然后你可以浏览DOM并提取所有属性的内部值。说真的,最多只需要10行代码就能实现。这是目前最棒的免费.NET库之一。

以下是一个示例:

            string htmlContents = new System.IO.StreamReader(resultsStream,Encoding.UTF8,true).ReadToEnd();

            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(htmlContents);
            if (doc == null) return null;

            string output = "";
            foreach (var node in doc.DocumentNode.ChildNodes)
            {
                output += node.InnerText;
            }

2
你甚至可以查询每个 text() 节点,修剪内容并使用空格将它们连接起来。IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim()) - jessehouwing
返回已翻译的文本:或者只需使用doc.DocumentNode.InnerText,尽管它似乎存在一些空格处理问题... - jessehouwing
17
为什么要进行 if (doc == null) 检查?这总是false,不是吗? 为什么要进行 if (doc == null) 检查?这总是false,不是吗? - avesse

68
Regex.Replace(htmlText, "<.*?>", string.Empty);

5
有很多问题-它不能处理带有<或>符号属性的内容,除非使用RegexOptions.SingleLine运行,否则对于跨越多行的标签处理不好。 - ChrisF
2
不要使用“<[^>]*>”。 - Paul Kienitz

11
protected string StripHtml(string Txt)
{
    return Regex.Replace(Txt, "<(.|\\n)*?>", string.Empty);
}    

Protected Function StripHtml(Txt as String) as String
    Return Regex.Replace(Txt, "<(.|\n)*?>", String.Empty)
End Function

2
不适用于许多情况,包括非Unix换行符。 - ChrisF

6

我已经在asp.net论坛上发布了这个问题,并且它似乎仍然是最简单的解决方案之一。我不能保证它是最快或最有效的,但它非常可靠。在.NET中,您可以使用HTML Web控件对象本身。你只需要将你的字符串插入一个临时的HTML对象,比如DIV,然后使用内置的'InnerText'来获取所有不包含在标签中的文本。以下是一个简单的C#示例:


System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;

这似乎不起作用,我使用简单的InnerHtml="<b>foo</b>"进行了测试;InnerText的值为"<b>foo</b>" :( - Axarydax
不要这样做。该解决方案直接将未编码的HTML注入输出中。这将使您面临跨站点脚本攻击的风险 - 您刚刚允许任何能够更改HTML字符串的人将任意HTML和JavaScript注入到您的应用程序中! - saille

5
我已经用C#编写了一个非常快的方法,比正则表达式更胜一筹。它托管在CodeProject上的一篇文章中。
它的优点包括更好的性能、替换命名和编号的HTML实体(如&amp;amp;&203;)以及注释块的替换等等。
请阅读CodeProject上的相关文章
谢谢。

4

如果您不能使用HtmlAgilityPack,.NET的XML读取器是一个选择。但这种方式可能在格式良好的HTML上失败,所以请始终添加一个带有正则表达式的catch语句作为备用方案。请注意,这并不快速,但确实提供了一个很好的机会来进行老派的逐步调试。

public static string RemoveHTMLTags(string content)
    {
        var cleaned = string.Empty;
        try
        {
            StringBuilder textOnly = new StringBuilder();
            using (var reader = XmlNodeReader.Create(new System.IO.StringReader("<xml>" + content + "</xml>")))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Text)
                        textOnly.Append(reader.ReadContentAsString());
                }
            }
            cleaned = textOnly.ToString();
        }
        catch
        {
            //A tag is probably not closed. fallback to regex string clean.
            string textOnly = string.Empty;
            Regex tagRemove = new Regex(@"<[^>]*(>|$)");
            Regex compressSpaces = new Regex(@"[\s\r\n]+");
            textOnly = tagRemove.Replace(content, string.Empty);
            textOnly = compressSpaces.Replace(textOnly, " ");
            cleaned = textOnly;
        }

        return cleaned;
    }

3
string result = Regex.Replace(anytext, @"<(.|\n)*?>", string.Empty);

2
我看了这里提出的基于正则表达式的解决方案,除了在最简单的情况下,它们并没有给我带来任何信心。在属性中的尖括号就足以导致错误,更不用说来自野外的格式错误的HTML了。那么像&amp;这样的实体又该怎么办呢?如果你想将HTML转换为纯文本,你需要解码实体。

因此,我提出以下方法。

使用HtmlAgilityPack,这个扩展方法可以高效地从html片段中删除所有HTML标记。还可以解码HTML实体,例如&amp;。仅返回内部文本项,并在每个文本项之间加入一个新行。

public static string RemoveHtmlTags(this string html)
{
        if (String.IsNullOrEmpty(html))
            return html;

        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);

        if (doc.DocumentNode == null || doc.DocumentNode.ChildNodes == null)
        {
            return WebUtility.HtmlDecode(html);
        }

        var sb = new StringBuilder();

        var i = 0;

        foreach (var node in doc.DocumentNode.ChildNodes)
        {
            var text = node.InnerText.SafeTrim();

            if (!String.IsNullOrEmpty(text))
            {
                sb.Append(text);

                if (i < doc.DocumentNode.ChildNodes.Count - 1)
                {
                    sb.Append(Environment.NewLine);
                }
            }

            i++;
        }

        var result = sb.ToString();

        return WebUtility.HtmlDecode(result);
}

public static string SafeTrim(this string str)
{
    if (str == null)
        return null;

    return str.Trim();
}

如果你真的很认真,你应该也会忽略某些HTML标签的内容(例如:<script><style><svg><head><object>),因为它们可能不包含我们想要的可读内容。你在这方面所做的将取决于你的情况和你想要达到的目标,但使用HtmlAgilityPack,白名单或黑名单选择标签将非常容易。
如果你将内容渲染回HTML页面,请确保了解XSS漏洞及其预防方法 - 即始终对呈现回HTML页面的任何用户输入文本进行编码(例如,> 变成 &gt; 等)。

1

对于那些抱怨Michael Tiptop的解决方案无法工作的人,这里是使用.Net4+的方法:

public static string StripTags(this string markup)
{
    try
    {
        StringReader sr = new StringReader(markup);
        XPathDocument doc;
        using (XmlReader xr = XmlReader.Create(sr,
                           new XmlReaderSettings()
                           {
                               ConformanceLevel = ConformanceLevel.Fragment
                               // for multiple roots
                           }))
        {
            doc = new XPathDocument(xr);
        }

        return doc.CreateNavigator().Value; // .Value is similar to .InnerText of  
                                           //  XmlDocument or JavaScript's innerText
    }
    catch
    {
        return string.Empty;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接