Używanie wyrażeń regularnych C # do usuwania tagów HTML

139

Jak używać wyrażenia regularnego C # do zastępowania / usuwania wszystkich tagów HTML, w tym nawiasów kątowych? Czy ktoś może mi pomóc z kodem?

Keltex
źródło
Nie wskazujesz tego, ale wnioskuję, że chcesz również całkowicie usunąć skrypty i elementy stylu, a nie tylko usunąć tag. Poniższa odpowiedź HTML Agility Pack jest poprawna do usuwania tagów, ale aby usunąć skrypt i styl, potrzebujesz również czegoś takiego jak stackoverflow.com/questions/13441470/ ...
Jan
1
Pytanie wskazane jako duplikat zawiera wiele informacji (i Tony the Pony!), Ale dotyczyło tylko otwierania tagów, a nie wszystkich tagów. Więc nie jestem pewien, czy technicznie jest to duplikat. To powiedziawszy, odpowiedź jest taka sama: nie.
goodeye

Odpowiedzi:

154

Jak często wspomniano wcześniej, nie należy używać wyrażeń regularnych do przetwarzania dokumentów XML lub HTML. Nie radzą sobie zbyt dobrze z dokumentami HTML i XML, ponieważ nie ma sposobu na ogólne wyrażenie zagnieżdżonych struktur.

Możesz użyć następującego.

String result = Regex.Replace(htmlDocument, @"<[^>]*>", String.Empty);

Będzie to działać w większości przypadków, ale w niektórych przypadkach (na przykład CDATA zawierający nawiasy ostre) nie będzie to działać zgodnie z oczekiwaniami.

Daniel Brückner
źródło
13
To naiwna implementacja. Oznacza to, że <div id = "x <4>"> jest niestety prawidłowym kodem HTML. Zajmuje się jednak większością rozsądnych spraw ...
Ryan Emerle
8
Jak już wspomniano, zdaję sobie sprawę, że to wyrażenie w niektórych przypadkach zawiedzie. Nie jestem nawet pewien, czy w ogólnym przypadku można obsłużyć dowolne wyrażenie regularne bez błędów.
Daniel Brückner
1
Nie, to zawiedzie we wszystkich przypadkach! jest chciwy.
Jake
13
@Cipher, dlaczego uważasz, że chciwość jest problemem? Zakładając, że dopasowanie zaczyna się na początku prawidłowego tagu HTML, nigdy nie wyjdzie poza koniec tego tagu. Do tego służy [^>].
Alan Moore
1
@AlanMoore html nie jest „zwykłym językiem”, tj. Nie można poprawnie dopasować wszystkiego, co jest poprawnym html do wyrażeń regularnych. patrz: stackoverflow.com/questions/590747/…
Kache
78

Prawidłowa odpowiedź brzmi: nie rób tego, użyj pakietu HTML Agility Pack .

Edytowano, aby dodać:

Aby bezwstydnie okraść poniższy komentarz jesse i nie zostać oskarżonym o niewystarczającą odpowiedź na pytanie po tak długim czasie, oto prosty, niezawodny fragment kodu wykorzystujący pakiet HTML Agility Pack, który działa nawet z najbardziej niedoskonałymi, kapryśnymi fragmentami HTML:

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(Properties.Resources.HtmlContents);
var text = doc.DocumentNode.SelectNodes("//body//text()").Select(node => node.InnerText);
StringBuilder output = new StringBuilder();
foreach (string line in text)
{
   output.AppendLine(line);
}
string textOnly = HttpUtility.HtmlDecode(output.ToString());

Istnieje bardzo niewiele możliwych do obronienia przypadków użycia wyrażenia regularnego do analizowania kodu HTML, ponieważ HTML nie może być poprawnie przeanalizowany bez świadomości kontekstowej, której zapewnienie jest bardzo bolesne nawet w nietradycyjnym silniku regex. Możesz dostać się tam częściowo za pomocą wyrażenia regularnego, ale musisz przeprowadzić ręczną weryfikację.

Html Agility Pack może zapewnić solidne rozwiązanie, które zmniejszy potrzebę ręcznego naprawiania aberracji, które mogą wynikać z naiwnego traktowania HTML jako gramatyki bezkontekstowej.

Wyrażenie regularne może w większości przypadków zapewniać to, czego potrzebujesz, ale w bardzo typowych przypadkach zawodzi. Jeśli możesz znaleźć lepszy / szybszy parser niż HTML Agility Pack, zrób to, ale nie narażaj świata na więcej zepsutego hakera HTML.

JasonTrue
źródło
27
HTML Agility Pack nie jest odpowiedzią na wszystko, co dotyczy pracy z HTML (np. Co jeśli chcesz pracować tylko z fragmentami kodu HTML ?!).
PropellerHead
7
Działa całkiem dobrze z fragmentami HTML i jest to najlepsza opcja dla scenariusza opisanego przez oryginalny plakat. Z drugiej strony Regex działa tylko z wyidealizowanym kodem HTML i zepsuje się z całkowicie poprawnym HTML, ponieważ gramatyka HTML nie jest regularna. Gdyby używał Rubiego, nadal zasugerowałbym nokogiri, hpricot lub beautifulsoup dla Pythona. Najlepiej traktować HTML jak HTML, a nie jakiś dowolny strumień tekstowy bez gramatyki.
JasonTrue
1
HTML nie jest zwykłą gramatyką i dlatego nie może być analizowany wyłącznie za pomocą wyrażeń regularnych. Możesz używać wyrażeń regularnych do leksowania, ale nie do analizowania. To naprawdę takie proste. Lingwiści zgodziliby się na to, zanim jeszcze istniał HTML.
JasonTrue
20
To nie jest kwestia opinii. Wyrażenie regularne może w większości przypadków zapewniać Ci to, czego potrzebujesz, ale w bardzo typowych przypadkach zawodzi. Jeśli możesz znaleźć lepszy / szybszy parser niż HTML Agility Pack, zrób to, ale nie narażaj świata na więcej zepsutego hakera HTML.
JasonTrue
2
Nie można poprawnie zidentyfikować znaczników HTML bez analizy kodu HTML. Czy rozumiesz całą gramatykę dotyczącą HTML? Zobacz złowrogi hack, aby zbliżyć się „całkiem blisko”, jak sugerują inne odpowiedzi, i powiedz mi, dlaczego chcesz to utrzymać. Odebranie mi głosu, ponieważ niefortunna, szybka próba zadziała na przykładowe dane wejściowe, nie spowoduje, że Twoje rozwiązanie będzie poprawne. Czasami używałem wyrażeń regularnych do generowania raportów z zawartości HTML lub do poprawiania niektórych odniesień CSS za pomocą dopasowania wykluczającego w & gt; aby ograniczyć ryzyko błędów, ale przeprowadziliśmy dodatkowe weryfikacje; to nie był cel ogólny.
JasonTrue
38

Pytanie jest zbyt szerokie, aby można było na nie odpowiedzieć ostatecznie. Czy mówisz o usunięciu wszystkich tagów z rzeczywistego dokumentu HTML, takiego jak strona internetowa? Jeśli tak, musisz:

  • usuń deklarację <! DOCTYPE lub <? xml prolog, jeśli istnieją
  • usuń wszystkie komentarze SGML
  • usuń cały element HEAD
  • usuń wszystkie elementy SCRIPT i STYLE
  • zrobić Grabthar-wie-co z elementami FORM i TABLE
  • usuń pozostałe tagi
  • usuń sekwencje <! [CDATA [i]]> z sekcji CDATA, ale pozostaw ich zawartość w spokoju

To tylko z głowy - jestem pewien, że jest więcej. Gdy już to zrobisz, w niektórych miejscach słowa, zdania i akapity będą biegły razem, aw innych duże fragmenty bezużytecznych spacji.

Ale zakładając, że pracujesz tylko z fragmentem i możesz uciec po prostu usuwając wszystkie tagi, oto wyrażenie regularne, którego użyłbym:

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"

Dopasowanie ciągów w pojedynczych i podwójnych cudzysłowach w ich własnych alternatywach wystarczy, aby rozwiązać problem nawiasów ostrych w wartościach atrybutów. Nie widzę potrzeby jawnego dopasowywania nazw atrybutów i innych rzeczy w tagu, jak robi to wyrażenie regularne w odpowiedzi Ryana; pierwsza alternatywa obsługuje to wszystko.

Jeśli zastanawiasz się nad tymi (?>...)konstrukcjami, są to grupy atomowe . Sprawiają, że wyrażenia regularne są trochę bardziej wydajne, ale co ważniejsze, zapobiegają niekontrolowanemu cofaniu się, na co zawsze należy uważać, gdy mieszasz przemienność i zagnieżdżone kwantyfikatory, tak jak to zrobiłem. Naprawdę nie sądzę, żeby to był problem, ale wiem, że jeśli o tym nie wspomnę, zrobi to ktoś inny. ;-)

To wyrażenie regularne nie jest oczywiście idealne, ale prawdopodobnie jest tak dobre, jak kiedykolwiek będziesz potrzebować.

Alan Moore
źródło
1
To zdecydowanie najlepsza odpowiedź. Odpowiadasz na pytanie nadawcy i wyjaśniasz, dlaczego w zadaniu nie należy używać wyrażenia regularnego. Dobra robota.
JWilliams
26
Regex regex = new Regex(@"</?\w+((\s+\w+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline);

Źródło

Ryan Emerle
źródło
18

@JasonTrue ma rację, że usuwanie znaczników HTML nie powinno odbywać się za pomocą wyrażeń regularnych.

Usunięcie tagów HTML za pomocą HtmlAgilityPack jest dość proste:

public string StripTags(string input) {
    var doc = new HtmlDocument();
    doc.LoadHtml(input ?? "");
    return doc.DocumentNode.InnerText;
}
zzzzBov
źródło
1
Chociaż jestem trochę spóźniony, chciałbym wspomnieć, że działa to również w przypadku XML, takiego jak ten produkowany przez Word i inne produkty biurowe. każdy, kto kiedykolwiek miał potrzebę radzenia sobie z Word xml, zrobiłby dobrze, gdyby spojrzał na użycie tego, ponieważ bardzo pomaga, zwłaszcza jeśli chcesz usunąć tagi z treści, co jest dokładnie tym, do czego go potrzebowałem.
Steve Pettifer
Kiedy wszystko inne wydawało się zawodzić, ten prosty fragment kodu uratował sytuację. Dzięki!
Ted Krapf
14

Chciałbym powtórzyć odpowiedź Jasona, chociaż czasami trzeba naiwnie przeanalizować jakiś kod HTML i wyciągnąć zawartość tekstową.

Musiałem to zrobić za pomocą jakiegoś HTML, który został stworzony przez edytor tekstu sformatowanego, zawsze zabawny i gry.

W takim przypadku może być konieczne usunięcie zawartości niektórych tagów, a także samych tagów.

W moim przypadku i tagi zostały wrzucone do tej mieszanki. Ktoś może uznać moją (nieco) mniej naiwną implementację za przydatny punkt wyjścia.

   /// <summary>
    /// Removes all html tags from string and leaves only plain text
    /// Removes content of <xml></xml> and <style></style> tags as aim to get text content not markup /meta data.
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string HtmlStrip(this string input)
    {
        input = Regex.Replace(input, "<style>(.|\n)*?</style>",string.Empty);
        input = Regex.Replace(input, @"<xml>(.|\n)*?</xml>", string.Empty); // remove all <xml></xml> tags and anything inbetween.  
        return Regex.Replace(input, @"<(.|\n)*?>", string.Empty); // remove any tags but not there content "<p>bob<span> johnson</span></p>" becomes "bob johnson"
    }
CountZero
źródło
1
Oprócz oczywistych problemów z łamaniem linii między platformami, posiadanie niezadowolonego kwantyfikatora jest powolne, gdy zawartość jest rozdzielana. Używaj rzeczy, jak <xml>.*(?!</xml>)</xml>z RegexOptions.SingleLinemodyfikatora przez pierwsze dwa i <[^>]*>na ostatnim. Pierwsze z nich można również łączyć poprzez przechwyconą przemianę w nazwie pierwszego znacznika i odwołania wsteczne do niego w negatywnym tagu wyprzedzającym i końcowym.
ChrisF
5

wypróbuj metodę wyrażeń regularnych pod tym adresem URL: http://www.dotnetperls.com/remove-html-tags

/// <summary>
/// Remove HTML from string with Regex.
/// </summary>
public static string StripTagsRegex(string source)
{
return Regex.Replace(source, "<.*?>", string.Empty);
}

/// <summary>
/// Compiled regular expression for performance.
/// </summary>
static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);

/// <summary>
/// Remove HTML from string with compiled Regex.
/// </summary>
public static string StripTagsRegexCompiled(string source)
{
return _htmlRegex.Replace(source, string.Empty);
}
Owidat
źródło
3

Użyj tego..

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"
Swaroop
źródło
-1

Użyj tej metody, aby usunąć tagi:

public string From_To(string text, string from, string to)
{
    if (text == null)
        return null;
    string pattern = @"" + from + ".*?" + to;
    Regex rx = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
    MatchCollection matches = rx.Matches(text);
    return matches.Count <= 0 ? text : matches.Cast<Match>().Where(match => !string.IsNullOrEmpty(match.Value)).Aggregate(text, (current, match) => current.Replace(match.Value, ""));
}
AnisNoorAli
źródło