Como retirar as tags HTML de uma string no ASP.NET?

123

Usando o ASP.NET, como posso retirar as tags HTML de uma determinada string de maneira confiável (ou seja, não usando regex)? Eu estou procurando algo como PHP strip_tags.

Exemplo:

<ul><li>Hello</li></ul>

Resultado:

"Olá"

Estou tentando não reinventar a roda, mas ainda não encontrei nada que atenda às minhas necessidades.

daniel
fonte
Eu imaginaria que o PHP strip_tags usa regex nos bastidores!
stevehipwell
10
@ Daniel: porque o regex é muito ruim nisso, especialmente se você tiver aninhamento.
Joel Coehoorn
Hmm, não se parece com strip_tags do PHP é particularmente fiável ou vai nas notas offical e os comentários: uk.php.net/strip_tags
Zhaph - Ben Duguid

Respostas:

112

Se estiver apenas retirando todas as tags HTML de uma sequência, isso funcionará de maneira confiável também com o regex. Substituir:

<[^>]*(>|$)

com a cadeia vazia, globalmente. Não se esqueça de normalizar a sequência posteriormente, substituindo:

[\s\r\n]+

com um único espaço e aparando o resultado. Opcionalmente, substitua quaisquer entidades de caracteres HTML de volta aos caracteres reais.

Nota :

  1. Há uma limitação: HTML e XML permitem >valores de atributo. Esta solução irá retornar marcação quebrado quando se deparam com tais valores.
  2. A solução é tecnicamente segura, como em: O resultado nunca conterá nada que possa ser usado para executar scripts entre sites ou interromper o layout de uma página. Não é apenas muito limpo.
  3. Como em todas as coisas em HTML e regex:
    use um analisador adequado se você precisar corrigi-lo em todas as circunstâncias.
Tomalak
fonte
52
Embora não seja solicitado, acho que muitos leitores também quererão descascar a codificação HTM, como &quote;. Eu o combino com WebUtility.HtmlDecodeisso (o que, por sua vez, não remove as tags). Use-o após a remoção da tag, pois pode reescrever &gt;e &lt;. ExemploWebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious
@YahooSerious Obrigado por fornecer um exemplo. Isso funciona muito bem. Obrigado.
SearchForKnowledge
O pacote de agilidade em HTML é o caminho a percorrer, eu o usei nos formulários da Web para remover páginas inteiras da Web para usar o conteúdo!
precisa
3
@YahooSerious, isso permitirá um vetor XSS no entanto & gt; script & lt; alerta ("XXS"); & gt; / script & lt; Não será higienizado pelo regex, mas convertido por HtmlDecode em alerta <script> ("XXS"); </ script>
1
@Heather Muito bom ponto. A remoção de tags HTML teria que ser feita novamente após a decodificação da entidade.
precisa
76

Vá baixar HTMLAgilityPack, agora! ;) Baixar LInk

Isso permite carregar e analisar o HTML. Em seguida, você pode navegar pelo DOM e extrair os valores internos de todos os atributos. Sério, você levará cerca de 10 linhas de código no máximo. É uma das maiores bibliotecas .net gratuitas disponíveis no mercado.

Aqui está uma amostra:

            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;
            }
Serapth
fonte
2
você pode até consultar todos os text()nós, aparar o conteúdo e a string. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
precisa saber é o seguinte
ou simplesmente usar doc.DocumentNode.InnerText, embora isso tem alguns problemas com whitespacehandling que parece ...
jessehouwing
17
Por que o if (doc == null)cheque? Isso é sempre falso, não é?
Avesse
67
Regex.Replace(htmlText, "<.*?>", string.Empty);
user95144
fonte
Simples e agradável. Obrigado!
Tillito
5
Tem muitos problemas - não lida com atributos com <ou> neles e não se dá bem com tags que abrangem mais de uma linha, a menos que sejam executadas com RegexOptions.SingleLine.
ChrisF
2
Não, use "<[^>] *>".
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
meramez
fonte
2
Não funciona em muitos casos, incluindo quebras de linha não-unix.
ChrisF
6

Eu publiquei isso nos fóruns do asp.net, e ainda parece ser uma das soluções mais fáceis por aí. Não garanto que seja o mais rápido ou mais eficiente, mas é bastante confiável. No .NET, você pode usar os próprios objetos HTML Web Control. Tudo o que você realmente precisa fazer é inserir sua string em um objeto HTML temporário, como um DIV, e usar o 'InnerText' interno para capturar todo o texto que não está contido nas tags. Veja abaixo um exemplo simples de C #:


System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;
Michael Tipton
fonte
isso não parece funcionar, eu testei com InnerHtml = "<b> foo </b>" simples; e InnerText tem o valor "<b> foo </b>" :(
Axarydax 11/11/11
Não faça isso. Esta solução injeta html não codificado diretamente na saída. Isso deixaria você aberto a ataques de Cross Site Scripting - você permitiu que qualquer pessoa que pudesse alterar a string html injetasse qualquer html e javascript arbitrário em seu aplicativo!
saille 27/05
5

Eu escrevi um método bastante rápido em c # que supera o Regex. Está hospedado em um artigo no CodeProject.

Suas vantagens são, entre melhor desempenho, a capacidade de substituir entidades HTML nomeadas e numeradas (aquelas como &amp;amp;e&203; ) e substituir blocos de comentários e muito mais.

Por favor, leia o artigo relacionado no CodeProject .

Obrigado.

Andrei Rînea
fonte
4

Para aqueles que não podem usar o HtmlAgilityPack, o leitor XML do .NETs é uma opção. Isso pode falhar em HTML bem formatado, portanto, sempre adicione uma captura com o regx como um backup. Observe que isso NÃO é rápido, mas fornece uma boa oportunidade para a etapa da velha escola na depuração.

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;
    }
Balde
fonte
3
string result = Regex.Replace(anytext, @"<(.|\n)*?>", string.Empty);
Alex
fonte
1

Para aqueles que estão se queixando de que a solução de Michael Tiptop não está funcionando, aqui está a maneira .Net4 + de fazer isso:

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;
    }
}
Annie
fonte
1
using System.Text.RegularExpressions;

string str = Regex.Replace(HttpUtility.HtmlDecode(HTMLString), "<.*?>", string.Empty);
Karan
fonte
0

Analisei as soluções baseadas em Regex sugeridas aqui e elas não me enchem de confiança, exceto nos casos mais triviais. Um colchete angular em um atributo é tudo o que seria necessário para quebrar, e muito menos HTML malformado. E o que dizer de entidades como&amp; ? Se você deseja converter HTML em texto sem formatação, também é necessário decodificar entidades.

Então, proponho o método abaixo.

Usando HtmlAgilityPack , esse método de extensão remove eficientemente todas as tags HTML de um fragmento html. Também decodifica entidades HTML como &amp;. Retorna apenas os itens de texto internos, com uma nova linha entre cada item de texto.

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();
}

Se você for realmente sério, você iria querer ignorar o conteúdo das etiquetas de certa HTML também ( <script>, <style>, <svg>, <head>,<object> vêm à mente!) Porque eles provavelmente não contêm conteúdo legível, no sentido de que são depois. O que você faz lá depende das suas circunstâncias e até onde você deseja ir, mas usar o HtmlAgilityPack seria bastante trivial para colocar as tags selecionadas na lista de permissões ou na lista negra.

Se você estiver renderizando o conteúdo de volta para uma página HTML, certifique-se de entender a vulnerabilidade XSS e como evitá-la - ou seja, sempre codifique qualquer texto inserido pelo usuário que seja renderizado novamente em uma página HTML ( >torna-se &gt;etc).

veleiro
fonte
0

Para o segundo parâmetro, ou seja, mantenha algumas tags, você pode precisar de um código como este usando HTMLagilityPack:

public string StripTags(HtmlNode documentNode, IList keepTags)
{
    var result = new StringBuilder();
        foreach (var childNode in documentNode.ChildNodes)
        {
            if (childNode.Name.ToLower() == "#text")
            {
                result.Append(childNode.InnerText);
            }
            else
            {
                if (!keepTags.Contains(childNode.Name.ToLower()))
                {
                    result.Append(StripTags(childNode, keepTags));
                }
                else
                {
                    result.Append(childNode.OuterHtml.Replace(childNode.InnerHtml, StripTags(childNode, keepTags)));
                }
            }
        }
        return result.ToString();
    }

Mais explicações nesta página: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/

Yuksel Daskin
fonte
0

Você também pode fazer isso com o AngleSharp, que é uma alternativa ao HtmlAgilityPack (não que o HAP seja ruim). É mais fácil usar do que o HAP para obter o texto de uma fonte HTML.

var parser = new HtmlParser();
var htmlDocument = parser.ParseDocument(source);
var text = htmlDocument.Body.Text();

Você pode dar uma olhada na seção de recursos principais , na qual eles afirmam ser "melhores" que o HAP. Eu acho que, na maior parte, provavelmente é um exagero para a pergunta atual, mas ainda assim, é uma alternativa interessante.

Yepeekai
fonte
-4

Basta usar string.StripHTML();

user3638478
fonte
3
Como o @Serpiton aponta, não existe esse método no BCL. Você poderia apontar para uma implementação desse método ou fornecer o seu próprio?
Sven Grosen