Come posso rimuovere i tag HTML da una stringa in ASP.NET?


123

Utilizzando ASP.NET, come posso rimuovere i tag HTML da una determinata stringa in modo affidabile (cioè senza usare regex)? Sto cercando qualcosa come PHP strip_tags.

Esempio:

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

Produzione:

"Ciao"

Sto cercando di non reinventare la ruota, ma finora non ho trovato nulla che soddisfi le mie esigenze.


Immagino che PHP strip_tags utilizzi regex dietro le quinte!
Stevehipwell

10
@Daniel: perché la regex è pessima in questo, soprattutto se hai l'annidamento.
Joel Coehoorn

Hmm, non sembra che Strip_Tags di PHP sia particolarmente affidabile sia nelle note ufficiali che nei commenti: uk.php.net/strip_tags
Zhaph - Ben Duguid

Risposte:


112

Se sta semplicemente rimuovendo tutti i tag HTML da una stringa, funziona modo affidabile anche con regex. Sostituire:

<[^>]*(>|$)

con la stringa vuota, globalmente. Non dimenticare di normalizzare la stringa in seguito, sostituendo:

[\s\r\n]+

con un solo spazio e ritagliando il risultato. Facoltativamente, sostituire qualsiasi entità carattere HTML con i caratteri effettivi.

Nota :

  1. C'è una limitazione: HTML e XML consentono > valori degli attributi. Questa soluzione si ritorno markup rotto quando incontrano tali valori.
  2. La soluzione è tecnicamente sicura, come in: Il risultato non conterrà mai nulla che potrebbe essere utilizzato per eseguire scripting cross site o per interrompere un layout di pagina. Semplicemente non è molto pulito.
  3. Come con tutte le cose HTML e regex:
    usa un parser appropriato se devi farlo bene in tutte le circostanze.

52
Sebbene non sia richiesto, penso che molti lettori vorranno anche rimuovere la codifica HTM, come &quote;. Lo combino con WebUtility.HtmlDecodeper quello (che a sua volta non rimuoverà i tag). Usalo dopo la rimozione del tag, poiché potrebbe riscrivere &gt;e &lt;. Ad esempioWebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious

@YahooSerious Grazie per aver fornito un esempio. Funziona alla grande. Grazie.
SearchForKnowledge

Html Agility Pack è la strada da percorrere, l'ho usato nel passato nei moduli web per rimuovere intere pagine web per utilizzare i contenuti!
Bojangles

3
@YahooSerious questo consentirà un vettore XSS tuttavia & gt; script & lt; alert ( "XXS"); & gt; / script & lt; Non verrà disinfettato dalla regex ma convertito da HtmlDecode in <script> alert ("XXS"); </ script>

1
@ Heather Punto molto buono. La rimozione dei tag HTML dovrebbe essere ripetuta dopo la decodifica dell'entità.
Tomalak

76

Vai a scaricare HTMLAgilityPack, ora! ;) Scarica LInk

Ciò ti consente di caricare e analizzare HTML. Quindi puoi navigare nel DOM ed estrarre i valori interni di tutti gli attributi. Seriamente, ci vorranno circa 10 righe di codice al massimo. È una delle più grandi librerie .net gratuite in circolazione.

Ecco un esempio:

            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
puoi persino interrogare ogni text()nodo, ritagliare il contenuto e la stringa. Unisciti a quelli con spazio. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing

o semplicemente usa doc.DocumentNode.InnerText, anche se questo ha alcuni problemi con la gestione degli spazi, sembra ...
jessehouwing

17
Perché l' if (doc == null)assegno? Questo è sempre falso, non è così?
avesse il

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

Semplice e simpatico. Grazie!
Tillito

5
Ha molti problemi: non si occupa degli attributi che contengono <o> e non funziona bene con i tag che si estendono su più di una riga a meno che non vengano eseguiti con RegexOptions.SingleLine.
ChrisF

2
Noooo, usa "<[^>] *>".
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
Non funziona per molti casi, comprese le interruzioni di riga non Unix.
ChrisF

6

L'ho pubblicato sui forum di asp.net e sembra ancora essere una delle soluzioni più semplici in circolazione. Non garantisco che sia il più veloce o più efficiente, ma è abbastanza affidabile. In .NET è possibile utilizzare gli oggetti HTML Web Control stessi. Tutto quello che devi fare è inserire la tua stringa in un oggetto HTML temporaneo come un DIV, quindi utilizzare "InnerText" incorporato per catturare tutto il testo che non è contenuto nei tag. Vedi sotto per un semplice esempio C #:


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

questo non sembra funzionare, l'ho testato con il semplice InnerHtml = "<b> foo </b>"; e InnerText ha il valore "<b> foo </b>" :(
Axarydax

Non farlo. Questa soluzione inietta html non codificato direttamente nell'output. Questo ti lascerebbe aperto agli attacchi di Cross Site Scripting: hai appena permesso a chiunque possa modificare la stringa html di iniettare qualsiasi html e javascript arbitrario nella tua applicazione!
salpa

5

Ho scritto un metodo piuttosto veloce in c # che batte a morte il Regex. È ospitato in un articolo su CodeProject.

I suoi vantaggi sono, tra le migliori prestazioni, la possibilità di sostituire entità HTML denominate e numerate (quelle come &amp;amp;e&203; ) e la sostituzione dei blocchi di commento e altro ancora.

Si prega di leggere l' articolo correlato su CodeProject .

Grazie.


4

Per quelli di voi che non possono utilizzare HtmlAgilityPack, il lettore XML .NET è un'opzione. Questo può fallire su HTML ben formattato, quindi aggiungi sempre un problema con regx come backup. Nota che NON è veloce, ma fornisce una bella opportunità per il passaggio della vecchia scuola attraverso il debug.

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

1

Per coloro che si complimentano per il fatto che la soluzione di Michael Tiptop non funziona, ecco il modo .Net4 + di farlo:

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

1
using System.Text.RegularExpressions;

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

0

Ho esaminato le soluzioni basate su Regex suggerite qui e non mi danno alcuna fiducia tranne nei casi più banali. Una parentesi angolare in un attributo è tutto ciò che serve per rompere, per non parlare dell'HTML mal formato dal selvaggio. E per quanto riguarda le entità come&amp; ? Se vuoi convertire l'HTML in testo normale, devi decodificare anche le entità.

Quindi propongo il metodo di seguito.

Utilizzando HtmlAgilityPack , questo metodo di estensione rimuove in modo efficiente tutti i tag HTML da un frammento html. Decodifica anche entità HTML come &amp;. Restituisce solo gli elementi di testo interni, con una nuova riga tra ogni elemento di testo.

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 siete veramente serio, che ci si vuole ignorare il contenuto dei tag HTML certo troppo ( <script>, <style>, <svg>, <head>,<object> vengono in mente!), Perché probabilmente non contengono contenuti leggibili nel senso ci sono dopo. Quello che farai dipenderà dalle tue circostanze e da quanto lontano vuoi andare, ma usando HtmlAgilityPack sarebbe piuttosto banale inserire nella whitelist o nella blacklist i tag selezionati.

Se stai restituendo il contenuto in una pagina HTML, assicurati di aver compreso la vulnerabilità XSS e come prevenirla, ovvero codifica sempre qualsiasi testo inserito dall'utente che viene restituito su una pagina HTML ( >diventa &gt;ecc.).


0

Per il secondo parametro, ovvero mantieni alcuni tag, potresti aver bisogno di un codice come questo 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();
    }

Maggiori spiegazioni su questa pagina: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/


0

Puoi farlo anche con AngleSharp che è un'alternativa a HtmlAgilityPack (non che HAP sia un male). È più facile da usare rispetto a HAP per ottenere il testo da una sorgente HTML.

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

Puoi dare un'occhiata alla sezione delle caratteristiche chiave in cui dimostrano di essere "migliori" di HAP. Penso che per la maggior parte sia probabilmente eccessivo per la domanda attuale, ma comunque è un'alternativa interessante.


-4

Basta usare string.StripHTML();


3
Come sottolinea @Serpiton, non esiste un tale metodo nella BCL. Potresti indicare un'implementazione di questo metodo o fornirne uno tuo?
Sven Grosen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.