Verwenden von regulären C # -Ausdrücken zum Entfernen von HTML-Tags

139

Wie verwende ich den regulären C # -Ausdruck, um alle HTML-Tags einschließlich der spitzen Klammern zu ersetzen / zu entfernen? Kann mir bitte jemand mit dem Code helfen?

Keltex
quelle
Sie geben es nicht an, aber ich schließe daraus, dass Sie auch Skript- und Stilelemente vollständig entfernen und nicht nur das Tag entfernen möchten. Die Antwort auf das HTML Agility Pack unten ist korrekt, um die Tags zu entfernen. Um jedoch Skript und Stil zu entfernen, benötigen Sie auch etwas wie stackoverflow.com/questions/13441470/…
John
1
Die als Duplikat angegebene Frage enthält viele Informationen (und Tony the Pony!), Es wurde jedoch nur nach dem Öffnen von Tags gefragt, nicht nach allen Tags. Ich bin mir also nicht sicher, ob es technisch gesehen ein Duplikat ist. Die Antwort ist jedoch dieselbe: nicht.
Goodeye

Antworten:

154

Wie bereits erwähnt, sollten Sie keine regulären Ausdrücke verwenden, um XML- oder HTML-Dokumente zu verarbeiten. Sie funktionieren mit HTML- und XML-Dokumenten nicht sehr gut, da verschachtelte Strukturen nicht allgemein ausgedrückt werden können.

Sie könnten Folgendes verwenden.

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

Dies funktioniert in den meisten Fällen, aber es gibt Fälle (z. B. CDATA mit spitzen Klammern), in denen dies nicht wie erwartet funktioniert.

Daniel Brückner
quelle
13
Dies ist eine naive Implementierung. Das heißt, <div id = "x <4>"> ist leider gültiges HTML. Behandelt die meisten vernünftigen Fälle ..
Ryan Emerle
8
Wie bereits erwähnt, ist mir bewusst, dass dieser Ausdruck in einigen Fällen fehlschlagen wird. Ich bin mir nicht einmal sicher, ob der allgemeine Fall durch einen regulären Ausdruck ohne Fehler behandelt werden kann.
Daniel Brückner
1
Nein, das wird in allen Fällen fehlschlagen! es ist gierig.
Jake
13
@Cipher, warum denkst du, ist Gier ein Problem? Angenommen, die Übereinstimmung beginnt am Anfang eines gültigen HTML-Tags und wird niemals über das Ende dieses Tags hinausgehen. Dafür ist das [^>] da.
Alan Moore
1
@AlanMoore HTML ist keine "normale Sprache", dh Sie können nicht alles, was gültiges HTML ist, mit regulären Ausdrücken abgleichen. siehe: stackoverflow.com/questions/590747/…
Kache
78

Die richtige Antwort lautet: Tun Sie das nicht. Verwenden Sie das HTML Agility Pack .

Bearbeitet, um hinzuzufügen:

Um den Kommentar von Jesse schamlos zu stehlen und zu vermeiden, dass er nach all der Zeit beschuldigt wird, die Frage nicht ausreichend beantwortet zu haben, finden Sie hier einen einfachen, zuverlässigen Ausschnitt mit dem HTML Agility Pack, der selbst mit den unvollständig geformten, launischen HTML-Elementen funktioniert:

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

Es gibt nur sehr wenige vertretbare Fälle für die Verwendung eines regulären Ausdrucks zum Parsen von HTML, da HTML ohne ein Kontextbewusstsein, das selbst in einer nicht-traditionellen Regex-Engine sehr schmerzhaft ist, nicht korrekt analysiert werden kann. Sie können mit einem RegEx auf halbem Weg dorthin gelangen, müssen jedoch manuelle Überprüfungen durchführen.

Html Agility Pack bietet Ihnen eine robuste Lösung, mit der Sie die Aberrationen, die sich aus der naiven Behandlung von HTML als kontextfreie Grammatik ergeben können, nicht mehr manuell beheben müssen.

Ein regulärer Ausdruck kann Ihnen meistens das bringen, was Sie wollen, aber in sehr häufigen Fällen schlägt er fehl. Wenn Sie einen besseren / schnelleren Parser als HTML Agility Pack finden, versuchen Sie es, aber setzen Sie die Welt nicht mehr kaputtem HTML-Hackery aus.

JasonTrue
quelle
27
HTML Agility Pack ist nicht die Antwort auf alles, was mit der Arbeit mit HTML zu tun hat (z. B. was ist, wenn Sie nur mit Fragmenten des HTML-Codes arbeiten möchten?!).
PropellerHead
7
Es funktioniert ziemlich gut mit HTML-Fragmenten und ist die beste Option für das im Originalposter beschriebene Szenario. Ein Regex hingegen funktioniert nur mit einem idealisierten HTML-Code und bricht mit perfekt gültigem HTML-Code, da die Grammatik von HTML nicht regelmäßig ist. Wenn er Ruby verwendet hätte, hätte ich immer noch Nokogiri oder Hpricot oder Beautifulsoup für Python vorgeschlagen. Es ist am besten, HTML wie HTML zu behandeln, nicht irgendeinen beliebigen Textstrom ohne Grammatik.
JasonTrue
1
HTML ist keine reguläre Grammatik und kann daher nicht nur mit regulären Ausdrücken analysiert werden. Sie können reguläre Ausdrücke zum Lexen verwenden, jedoch nicht zum Parsen. So einfach ist das wirklich. Linguisten hätten dem zugestimmt, bevor es überhaupt HTML gab.
JasonTrue
20
Dies ist keine Ansichtssache. Ein regulärer Ausdruck kann Ihnen meistens das bringen, was Sie wollen, aber in sehr häufigen Fällen schlägt er fehl. Wenn Sie einen besseren / schnelleren Parser als HTML Agility Pack finden, versuchen Sie es, aber setzen Sie die Welt bitte nicht mehr kaputtem HTML-Hackery aus.
JasonTrue
2
Sie können HTML-Tags nicht zuverlässig identifizieren, ohne HTML zu analysieren. Verstehst du die gesamte Grammatik für HTML? Sehen Sie sich den bösen Hack an, um "ziemlich nah" zu kommen, den andere Antworten vorschlagen, und sagen Sie mir, warum Sie das beibehalten möchten. Wenn Sie mich herabstimmen, weil ein hackiger schneller Versuch für Ihre Beispieleingabe funktioniert, wird Ihre Lösung nicht korrekt. Ich habe gelegentlich reguläre Ausdrücke verwendet, um Berichte aus HTML-Inhalten zu generieren oder um eine CSS-Referenz mithilfe eines negativen Abgleichs auf & gt; um die Wahrscheinlichkeit von Fehlern zu begrenzen, aber wir haben zusätzliche Überprüfungen durchgeführt; es war kein allgemeiner Zweck.
JasonTrue
38

Die Frage ist zu weit gefasst, um endgültig beantwortet zu werden. Sprechen Sie über das Entfernen aller Tags aus einem realen HTML-Dokument wie einer Webseite? Wenn ja, müssten Sie:

  • Entfernen Sie die <! DOCTYPE-Deklaration oder das <? xml-Prolog, falls vorhanden
  • Entfernen Sie alle SGML-Kommentare
  • Entfernen Sie das gesamte HEAD-Element
  • Entfernen Sie alle SCRIPT- und STYLE-Elemente
  • Mach Grabthar-weiß-was mit FORM- und TABLE-Elementen
  • Entfernen Sie die verbleibenden Tags
  • Entfernen Sie die <! [CDATA [und]]> Sequenzen aus den CDATA-Abschnitten, lassen Sie jedoch deren Inhalt in Ruhe

Das ist mir ein Rätsel - ich bin mir sicher, dass es noch mehr gibt. Wenn Sie das alles erledigt haben, werden an einigen Stellen Wörter, Sätze und Absätze zusammenlaufen und an anderen große Stücke nutzloser Leerzeichen.

Angenommen, Sie arbeiten nur mit einem Fragment und können alle Tags einfach entfernen. Hier ist der reguläre Ausdruck, den ich verwenden würde:

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

Das Anpassen von Zeichenfolgen in einfachen und doppelten Anführungszeichen in ihren eigenen Alternativen reicht aus, um das Problem der spitzen Klammern in Attributwerten zu lösen. Ich sehe keine Notwendigkeit, die Attributnamen und andere Dinge im Tag explizit abzugleichen, wie es der reguläre Ausdruck in Ryans Antwort tut. Die erste Alternative erledigt all das.

Falls Sie sich über diese (?>...)Konstrukte wundern , handelt es sich um Atomgruppen . Sie machen den Regex ein wenig effizienter, aber was noch wichtiger ist, sie verhindern ein außer Kontrolle geratenes Backtracking, worauf Sie immer achten sollten, wenn Sie wie ich Alternation und verschachtelte Quantifizierer mischen. Ich denke nicht wirklich, dass das hier ein Problem wäre, aber ich weiß, wenn ich es nicht erwähne, wird es jemand anderes tun. ;-);

Diese Regex ist natürlich nicht perfekt, aber wahrscheinlich so gut, wie Sie es jemals brauchen werden.

Alan Moore
quelle
1
Dies ist bei weitem die beste Antwort. Sie beantworten die Frage des Posters und erklären, warum für die jeweilige Aufgabe kein regulärer Ausdruck verwendet werden sollte. Gut gemacht.
JWilliams
26
Regex regex = new Regex(@"</?\w+((\s+\w+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline);

Quelle

Ryan Emerle
quelle
18

@JasonTrue ist richtig, dass das Entfernen von HTML-Tags nicht über reguläre Ausdrücke erfolgen sollte.

Es ist ganz einfach, HTML-Tags mit HtmlAgilityPack zu entfernen:

public string StripTags(string input) {
    var doc = new HtmlDocument();
    doc.LoadHtml(input ?? "");
    return doc.DocumentNode.InnerText;
}
zzzzBov
quelle
1
Obwohl ich etwas spät dran bin, möchte ich erwähnen, dass dies auch auf XML funktioniert, wie es von Word und anderen Office-Produkten produziert wird. Jeder, der jemals die Notwendigkeit hatte, sich mit Word xml zu befassen, sollte sich die Verwendung dieser Option ansehen, da dies sehr hilfreich ist, insbesondere wenn Sie Tags aus Inhalten entfernen müssen, für die ich sie genau benötigt habe.
Steve Pettifer
Als alles andere zu scheitern schien, rettete dieses einfache Code-Snippet den Tag. Vielen Dank!
Ted Krapf
13

Ich möchte Jasons Antwort wiederholen, obwohl Sie manchmal naiv etwas HTML analysieren und den Textinhalt herausziehen müssen.

Ich musste dies mit etwas HTML tun, das von einem Rich-Text-Editor erstellt wurde, immer Spaß und Spiel.

In diesem Fall müssen Sie möglicherweise den Inhalt einiger Tags sowie nur die Tags selbst entfernen.

In meinem Fall wurden Tags in diese Mischung geworfen. Jemand mag meine (sehr geringfügig) weniger naive Implementierung als nützlichen Ausgangspunkt betrachten.

   /// <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
quelle
1
Abgesehen von offensichtlichen plattformübergreifenden Problemen mit Zeilenumbrüchen ist ein ungreedy Quantifizierer langsam, wenn der Inhalt begrenzt ist. Verwenden Sie Dinge wie <xml>.*(?!</xml>)</xml>mit dem RegexOptions.SingleLineModifikator für die ersten beiden und <[^>]*>für die letzten. Die ersten können auch durch eine erfasste Abwechslung im Namen des ersten Tags und Rückverweise darauf im negativen Lookahead und im letzten Tag kombiniert werden.
ChrisF
5

Versuchen Sie es mit der Methode für reguläre Ausdrücke unter folgender 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
quelle
3

benutze das..

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

Verwenden Sie diese Methode, um Tags zu entfernen:

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
quelle