Comment puis-je supprimer les balises HTML d'une chaîne dans ASP.NET?

123

En utilisant ASP.NET, comment puis-je supprimer les balises HTML d'une chaîne donnée de manière fiable (c'est-à-dire sans utiliser de regex)? Je recherche quelque chose comme PHP strip_tags.

Exemple:

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

Production:

"Bonjour"

J'essaie de ne pas réinventer la roue, mais je n'ai rien trouvé qui réponde à mes besoins jusqu'à présent.

Daniel
la source
J'imagine que PHP strip_tags utilise regex dans les coulisses!
stevehipwell
10
@Daniel: parce que regex est très mauvais pour ça, surtout si vous avez une imbrication.
Joel Coehoorn
Hmm, cela ne ressemble pas à Strip_Tags de PHP est particulièrement fiable, que ce soit sur les notes officielles et les commentaires: uk.php.net/strip_tags
Zhaph - Ben Duguid

Réponses:

112

S'il s'agit simplement de supprimer toutes les balises HTML d'une chaîne, cela fonctionne également de manière fiable avec regex. Remplacer:

<[^>]*(>|$)

avec la chaîne vide, globalement. N'oubliez pas de normaliser la chaîne par la suite, en remplaçant:

[\s\r\n]+

avec un seul espace et découper le résultat. Remplacez éventuellement les entités de caractères HTML par les caractères réels.

Remarque :

  1. Il existe une limitation: HTML et XML autorisent les >valeurs d'attribut. Cette solution sera de retour balisage cassé lors de la rencontre de telles valeurs.
  2. La solution est techniquement sûre, comme dans: Le résultat ne contiendra jamais rien qui pourrait être utilisé pour faire des scripts intersites ou pour casser une mise en page. Ce n'est tout simplement pas très propre.
  3. Comme pour tout HTML et regex:
    utilisez un analyseur approprié si vous devez faire les choses correctement en toutes circonstances.
Tomalak
la source
52
Bien que cela ne soit pas demandé, je pense que beaucoup de lecteurs voudront également supprimer l'encodage HTM, comme &quote;. Je le combine avec WebUtility.HtmlDecodepour cela (qui à son tour ne supprimera pas les balises). Utilisez-le après la suppression des balises, car il peut réécrire &gt;et &lt;. Par exempleWebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious
@YahooSerious Merci d'avoir fourni un exemple. Cela fonctionne très bien. Je vous remercie.
SearchForKnowledge
Html Agility Pack est la voie à suivre, je l'ai utilisé dans les formulaires Web pour supprimer des pages Web entières pour utiliser du contenu!
Bojangles
3
@YahooSerious cela permettra un vecteur XSS dans cependant & gt; script & lt; alerte ("XXS"); & gt; / script & lt; Ne sera pas nettoyé par l'expression régulière mais converti par HtmlDecode en <script> alert ("XXS"); </ script>
1
@Heather Très bon point. Le dépouillement des balises HTML devrait être refait après le décodage de l'entité.
Tomalak
76

Allez télécharger HTMLAgilityPack, maintenant! ;) Télécharger LInk

Cela vous permet de charger et d'analyser le HTML. Ensuite, vous pouvez naviguer dans le DOM et extraire les valeurs internes de tous les attributs. Sérieusement, cela vous prendra environ 10 lignes de code au maximum. C'est l'une des plus grandes bibliothèques .net gratuites.

Voici un exemple:

            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
la source
2
vous pouvez même interroger chaque text()nœud, découper le contenu et la chaîne. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing
ou utilisez simplement doc.DocumentNode.InnerText, bien que cela pose quelques problèmes avec la
gestion des
17
Pourquoi le if (doc == null)chèque? C'est toujours faux, non?
avesse
67
Regex.Replace(htmlText, "<.*?>", string.Empty);
user95144
la source
Simple et agréable. Merci!
Tillito
5
A de nombreux problèmes - ne traite pas les attributs contenant <ou> et ne fonctionne pas bien avec les balises qui s'étendent sur plus d'une ligne sauf si elles sont exécutées avec RegexOptions.SingleLine.
ChrisF
2
Non, utilisez "<[^>] *>".
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
la source
2
Ne fonctionne pas dans de nombreux cas, y compris les sauts de ligne non-Unix.
ChrisF
6

J'ai posté ceci sur les forums asp.net, et cela semble toujours être l'une des solutions les plus simples. Je ne garantis pas que ce soit le plus rapide ou le plus efficace, mais c'est assez fiable. Dans .NET, vous pouvez utiliser les objets HTML Web Control eux-mêmes. Tout ce que vous avez vraiment à faire est d'insérer votre chaîne dans un objet HTML temporaire tel qu'un DIV, puis d'utiliser le "InnerText" intégré pour saisir tout le texte qui n'est pas contenu dans les balises. Voir ci-dessous pour un exemple C # simple:


System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;
Michael Tipton
la source
cela ne semble pas fonctionner, je l'ai testé avec un simple InnerHtml = "<b> foo </b>"; et InnerText a la valeur "<b> foo </b>" :(
Axarydax
Ne fais pas ça. Cette solution injecte du code HTML non codé directement dans la sortie. Cela vous laisserait largement ouvert aux attaques de type Cross Site Scripting - vous venez de permettre à quiconque peut changer la chaîne html d'injecter n'importe quel html et javascript arbitraire dans votre application!
saille
5

J'ai écrit une méthode assez rapide en c # qui bat l'enfer hors du Regex. Il est hébergé dans un article sur CodeProject.

Ses avantages sont, parmi de meilleures performances, la possibilité de remplacer les entités HTML nommées et numérotées (comme &amp;amp;et &203;) et le remplacement des blocs de commentaires et plus encore.

Veuillez lire l' article connexe sur CodeProject .

Je vous remercie.

Andrei Rînea
la source
4

Pour ceux d'entre vous qui ne peuvent pas utiliser HtmlAgilityPack, le lecteur XML .NETs est une option. Cela peut échouer sur du HTML bien formaté, alors ajoutez toujours une capture avec regx comme sauvegarde. Notez que ce n'est PAS rapide, mais cela offre une belle opportunité pour le débogage à l'ancienne.

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

Pour ceux qui se plaignent du fait que la solution de Michael Tiptop ne fonctionne pas, voici la façon .Net4 + de le faire:

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
la source
1
using System.Text.RegularExpressions;

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

J'ai regardé les solutions basées sur Regex suggérées ici, et elles ne me remplissent pas de confiance sauf dans les cas les plus triviaux. Un crochet angulaire dans un attribut est tout ce qu'il faudrait pour casser, sans parler du HTML mal formé à partir de la nature. Et qu'en est-il des entités comme &amp;? Si vous souhaitez convertir du HTML en texte brut, vous devez également décoder les entités.

Je propose donc la méthode ci-dessous.

En utilisant HtmlAgilityPack , cette méthode d'extension supprime efficacement toutes les balises HTML d'un fragment html. Décode également les entités HTML comme &amp;. Renvoie uniquement les éléments de texte internes, avec une nouvelle ligne entre chaque élément de texte.

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

Si vous êtes vraiment sérieux, vous voulez ignorer le contenu de certaines balises HTML trop ( <script>, <style>, <svg>, <head>, <object>viennent à l' esprit!) Parce qu'ils ne contiennent probablement un contenu lisible dans le sens que nous sommes après. Ce que vous faites là-bas dépendra de votre situation et de la distance que vous souhaitez parcourir, mais en utilisant HtmlAgilityPack, il serait assez simple de mettre sur liste blanche ou sur liste noire les balises sélectionnées.

Si vous restaurez le contenu sur une page HTML, assurez-vous de comprendre la vulnérabilité XSS et comment l'empêcher - c'est-à-dire toujours encoder tout texte saisi par l'utilisateur qui est rendu sur une page HTML ( >devient &gt;etc.).

saille
la source
0

Pour le deuxième paramètre, c'est-à-dire conserver certaines balises, vous aurez peut-être besoin d'un code comme celui-ci en utilisant 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();
    }

Plus d'explications sur cette page: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/

Yuksel Daskin
la source
0

Vous pouvez également le faire avec AngleSharp qui est une alternative à HtmlAgilityPack (pas que HAP soit mauvais). Il est plus facile d'utiliser que HAP pour extraire le texte d'une source HTML.

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

Vous pouvez jeter un oeil à la section des fonctionnalités clés où ils plaident pour être «meilleur» que HAP. Je pense que pour la plupart, c'est probablement exagéré pour la question actuelle, mais c'est quand même une alternative intéressante.

Yepeekai
la source
-4

Utilisez simplement string.StripHTML();

user3638478
la source
3
Comme le souligne @Serpiton, il n'y a pas une telle méthode dans la BCL. Pourriez-vous indiquer une implémentation de cette méthode ou fournir la vôtre?
Sven Grosen