Jak filtrować wszystkie znaczniki HTML z wyjątkiem określonej białej listy?

To jest dla .NET. IgnoreCase jest ustawione, a MultiLine nie jest ustawione.

Zazwyczaj jestem przyzwoity w regex, może kończy mi się kofeina...

Użytkownicy mogą wprowadzać encje kodowane HTML (

u, i, b, h3, h4, br, a, img

Samozamykające się
i są dozwolone, z dodatkową przestrzenią lub bez niej, ale nie są wymagane.

Chcę:

  1. usuwa wszystkie początkowe i końcowe znaczniki HTML inne niż wymienione powyżej.
  2. Usuń atrybuty z pozostałych tagów, z wyjątkiem kotwice mogą mieć href.

Mój wzorzec wyszukiwania (zastąpiony pustym ciągiem) na razie:

<(?!i|b|h3|h4|a|img|/i|/b|/h3|/h4|/a|/img)[^>]+>

To wydaje się usuwać wszystkie oprócz znaczników początku i końca, które chcę, ale są trzy problemy:

  1. dołączanie wersji znacznika końcowego każdego dozwolonego znacznika jest brzydkie.
  2. atrybuty przetrwają. Czy można to zrobić za pomocą jednego zamiennika?
  3. Tagi początek z dozwolone nazwy tagów wymykają się. Np. " " i " ".

Następujący sugerowany wzór nie usuwa znaczników, które nie mają atrybutów.

</?(?!i|b|h3|h4|a|img)\b[^>]*>

Jak wspomniano poniżej, " > " jest legalne w wartości atrybutu, ale można śmiało powiedzieć, że nie będę tego wspierać. Ponadto, nie będzie żadnych bloków CDATA, itp. żeby się martwić. Tylko trochę HTML.

Odpowiedź luki jest najlepsza jak na razie, dzięki! Oto jego wzór (mam nadzieję, że PRE działa lepiej dla ja): {]}

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

Niektóre drobne poprawki myślę, że można jeszcze zrobić do tej odpowiedzi:

  1. Myślę, że można to zmodyfikować, aby przechwytywać proste komentarze HTML (te, które same nie zawierają tagów), dodając "!-- "do zmiennej" akceptowalnej "i dokonując małej zmiany na końcu wyrażenia, aby umożliwić opcjonalne końcowe"\s--".

  2. Myślę, że byłoby to złamane, jeśli istnieje wiele białych znaków między atrybutami (przykład: mocno sformatowane HTML z podziałem linii i tabulatorami pomiędzy atrybutami).

Edit 2009-07-23: oto ostateczne rozwiązanie, które wybrałem (w VB.NET"): {]}

 Dim AcceptableTags As String = "i|b|u|sup|sub|ol|ul|li|br|h2|h3|h4|h5|span|div|p|a|img|blockquote"
 Dim WhiteListPattern As String = "</?(?(?=" & AcceptableTags & _
      ")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>"
 html = Regex.Replace(html, WhiteListPattern, "", RegExOptions.Compiled)

Zastrzeżenie jest takie, że atrybut href znaczników jest nadal usuwany, co nie jest idealne.

Author: richardtallent, 2008-11-21

7 answers

Oto funkcja, którą napisałem do tego zadania:

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

Edit: z jakiegoś powodu zamieściłem poprawkę do mojej poprzedniej odpowiedzi jako osobną odpowiedź, więc konsoliduję je tutaj.

Wyjaśnię trochę regex, ponieważ jest trochę długi.

Pierwsza część pasuje do otwartego wspornika i ukośników 0 LUB 1(w przypadku, gdy jest to bliski znacznik).

Następnie zobaczysz konstrukcję if-then z spojrzeniem w przyszłość. (?(?= SomeTag) then / else) sprawdzam czy następna część łańcucha jest jeden z akceptowalnych tagów. Widać, że łączę łańcuch regex z akceptowalną zmienną, która jest akceptowalnymi nazwami znaczników oddzielonymi pionowym paskiem, tak aby którykolwiek z terminów pasował. Jeśli jest to dopasowanie, widać, że wstawiłem słowo "notag", ponieważ żaden tag nie pasuje do tego i jeśli jest to dopuszczalne, chcę go zostawić w spokoju. W przeciwnym razie przechodzę do części else, gdzie dopasowuję dowolną nazwę znacznika [a-z,A-Z, 0-9] +

Następnie chcę dopasować 0 lub więcej atrybutów, które zakładam, że są w postaci atrybut= "wartość". więc teraz grupuję tę część reprezentującą atrybut, ale używam ?: (?: \s [a-z,a-z,0-9, -]+=?(?:(["",']?).?\1?))

Tutaj zaczynam od znaku spacji, który znajdowałby się między tagiem a nazwami atrybutów, a następnie dopasowuję nazwę atrybutu: [a-z, A-Z, 0-9, -] +

Następnie dopasowuję znak równości, a następnie cytat. Grupuję cytat, aby został przechwycony i mogę zrobić backreferencję później \1, aby dopasować ten sam typ cytatu. Pomiędzy tymi dwoma cytatami widać, że używam kropki, aby dopasować cokolwiek, jednak używam leniwej wersji*? zamiast chciwej wersji * tak, że będzie pasować tylko do następnego cytatu, który zakończy tę wartość.

Następnie umieszczamy * po zamknięciu grup nawiasami, aby pasowały do wielu kombinacji attirbute / value (lub none). Ostatnio dopasowujemy kilka białych spacji z \s, I 0 LUB 1 kończące ukośniki w znaczniku dla XML style self zamykanie tagów.

Widzisz, że zamieniam znaczniki na kiełbasę, bo jestem głodny, ale możesz też zastąpić je pustym ciągiem, aby je po prostu wyczyścić.

 28
Author: Loophole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-19 20:42:27

Jest to dobry przykład na filtrowanie znaczników html:

Dezynfekcja HTML

 11
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-23 18:06:48

Atrybuty są głównym problemem z używaniem wyrażeń regularnych do pracy z HTML. Weźmy pod uwagę samą liczbę potencjalnych atrybutów, fakt, że większość z nich jest opcjonalna, a także fakt, że mogą występować w dowolnej kolejności, oraz fakt, że " > " jest znakiem prawnym w cytowanych wartościach atrybutów. Kiedy zaczniesz próbować wziąć to wszystko pod uwagę, regex, którego potrzebujesz, aby poradzić sobie z tym wszystkim, szybko stanie się niemożliwy do zarządzania.

Zamiast tego chciałbym użyć HTML opartego na zdarzeniach parser, czyli taki, który daje Ci drzewo DOM, przez które możesz przejść.

 2
Author: Sherm Pendley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-20 22:07:56

Właśnie zauważyłem, że obecne rozwiązanie pozwala tagom, które zaczynają się od dowolnego z akceptowalnych tagów. Tak więc, jeśli" b "jest akceptowalnym znacznikiem, to "blink"również. Nie jest to wielka sprawa, ale coś do rozważenia, jeśli jesteś surowy o tym, jak filtrować HTML. Z pewnością nie chciałbyś dopuścić "s "jako akceptowalnego znacznika, ponieważ zezwalałby on na"script".

 2
Author: richardtallent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-16 20:59:51

Powodem, dla którego dodanie słowa boundary \b nie zadziałało, jest to, że nie umieściłeś go w lookahead. Tak więc, \b będzie próbowane po

Włóż go do spojrzenia w ten sposób:

<(?!/?(i|b|h3|h4|a|img)\b)[^>]+>

Pokazuje również, jak można umieścić / przed listą tagów, a nie z każdym tagiem.

 1
Author: Jan Goyvaerts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 10:34:45
    /// <summary>
    /// Trims the ignoring spacified tags
    /// </summary>
    /// <param name="text">the text from which html is to be removed</param>
    /// <param name="isRemoveScript">specify if you want to remove scripts</param>
    /// <param name="ignorableTags">specify the tags that are to be ignored while stripping</param>
    /// <returns>Stripped Text</returns>
    public static string StripHtml(string text, bool isRemoveScript, params string[] ignorableTags)
    {
        if (!string.IsNullOrEmpty(text))
        {
            text = text.Replace("&lt;", "<");
            text = text.Replace("&gt;", ">");
            string ignorePattern = null;

            if (isRemoveScript)
            {
                text = Regex.Replace(text, "<script[^<]*</script>", string.Empty, RegexOptions.IgnoreCase);
            }
            if (!ignorableTags.Contains("style"))
            {
                text = Regex.Replace(text, "<style[^<]*</style>", string.Empty, RegexOptions.IgnoreCase);
            }
            foreach (string tag in ignorableTags)
            {
                //the character b spoils the regex so replace it with strong
                if (tag.Equals("b"))
                {
                    text = text.Replace("<b>", "<strong>");
                    text = text.Replace("</b>", "</strong>");
                    if (ignorableTags.Contains("strong"))
                    {
                        ignorePattern = string.Format("{0}(?!strong)(?!/strong)", ignorePattern);
                    }
                }
                else
                {
                    //Create ignore pattern fo the tags to ignore
                    ignorePattern = string.Format("{0}(?!{1})(?!/{1})", ignorePattern, tag);
                }

            }
            //finally add the ignore pattern into regex <[^<]*> which is used to match all html tags
            ignorePattern = string.Format(@"<{0}[^<]*>", ignorePattern);
            text = Regex.Replace(text, ignorePattern, "", RegexOptions.IgnoreCase);
        }

        return text;
    }
 1
Author: Chirag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-08 11:45:57

Myślę, że pierwotnie chciałem, aby wartości były opcjonalne, ale nie poszło zgodnie z planem, ponieważ widzę, że dodałem ? po znaku równości i pogrupowałem część wartości dopasowania. Dodajmy ? po tej grupie (oznaczonej karotem), aby była opcjonalna również w meczu. Nie jestem teraz na moim kompilatorze, ale zobacz, czy to działa:

@"</?(?(?=" + acceptable + @")notag|[a-z,A-Z,0-9]+)(?:\s[a-z,A-Z,0-9,\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
                                                                                             ^
 0
Author: Loophole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-24 23:10:00