Làm cách nào tôi có thể tách các thẻ HTML từ một chuỗi trong ASP.NET?


123

Sử dụng ASP.NET, làm cách nào tôi có thể tách các thẻ HTML khỏi một chuỗi đã cho một cách đáng tin cậy (nghĩa là không sử dụng regex)? Tôi đang tìm kiếm một cái gì đó giống như của PHP strip_tags.

Thí dụ:

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

Đầu ra:

"Xin chào"

Tôi đang cố gắng không phát minh lại bánh xe, nhưng tôi chưa tìm thấy bất cứ điều gì đáp ứng nhu cầu của tôi cho đến nay.


Tôi sẽ tưởng tượng rằng các dải phân cách PHP sử dụng regex đằng sau hậu trường!
stevehipwell

10
@Daniel: vì regex rất tệ ở đó, đặc biệt nếu bạn có lồng.
Joel Coehoorn

Hmm, không có vẻ như Strip_Tags của PHP đặc biệt đáng tin cậy khi ghi chú chính thức và các nhận xét: uk.php.net/strip_tags
- Ben Duguid

Câu trả lời:


112

Nếu nó chỉ tước tất cả các thẻ HTML từ một chuỗi, thì điều này cũng hoạt động đáng tin cậy với regex. Thay thế:

<[^>]*(>|$)

với chuỗi rỗng, trên toàn cầu. Đừng quên bình thường hóa chuỗi sau đó, thay thế:

[\s\r\n]+

với một không gian duy nhất, và cắt tỉa kết quả. Tùy chọn thay thế bất kỳ thực thể ký tự HTML nào trở lại các ký tự thực tế.

Lưu ý :

  1. Có một hạn chế: HTML và XML cho phép >trong các giá trị thuộc tính. Giải pháp này sẽ trả về đánh dấu bị hỏng khi gặp các giá trị như vậy.
  2. Giải pháp này an toàn về mặt kỹ thuật, như trong: Kết quả sẽ không bao giờ chứa bất cứ thứ gì có thể được sử dụng để thực hiện kịch bản chéo trang hoặc phá vỡ bố cục trang. Nó chỉ là không sạch sẽ.
  3. Như với tất cả mọi thứ HTML và regex:
    Sử dụng trình phân tích cú pháp phù hợp nếu bạn phải làm cho đúng trong mọi trường hợp.

52
Mặc dù không được yêu cầu, tôi nghĩ rằng rất nhiều độc giả cũng muốn loại bỏ mã hóa HTM, như thế nào &quote;. Tôi kết hợp nó với WebUtility.HtmlDecodeđiều đó (lần lượt sẽ không xóa thẻ). Sử dụng nó sau khi loại bỏ thẻ, vì nó có thể viết lại &gt;&lt;. Ví dụ:WebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo nghiêm trọng

@YahooSerious Cảm ơn bạn đã cung cấp một ví dụ. Điều này làm việc tuyệt vời. Cảm ơn bạn.
SearchForKnowledge

Html Agility Pack là cách tốt nhất, tôi đã sử dụng nó trong các biểu mẫu web để loại bỏ toàn bộ trang web để sử dụng nội dung!
Bojangles

3
@YahooSerious điều này sẽ cho phép một vectơ XSS tuy nhiên & gt; kịch bản & lt; cảnh báo ("XXS"); & gt; / tập lệnh & lt; Sẽ không được vệ sinh bởi regex mà được chuyển đổi bởi HtmlDecode thành <script> alert ("XXS"); </ script>

1
@Heather Điểm rất tốt. Tước thẻ HTML sẽ phải được thực hiện lại sau khi giải mã thực thể.
Tomalak

76

Hãy tải xuống HTMLAgilityPack ngay bây giờ! ;) Tải xuống LInk

Điều này cho phép bạn tải và phân tích HTML. Sau đó, bạn có thể điều hướng DOM và trích xuất các giá trị bên trong của tất cả các thuộc tính. Nghiêm túc, nó sẽ đưa bạn tối đa khoảng 10 dòng mã. Đây là một trong những thư viện .net miễn phí lớn nhất hiện có.

Đây là một mẫu:

            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
bạn thậm chí có thể truy vấn mọi text()nút, cắt nội dung và chuỗi. Tham gia vào các không gian. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jliehouwing

hoặc chỉ đơn giản là sử dụng doc.DocumentNode.InnerText, mặc dù điều này có một số vấn đề với khoảng trắng có vẻ như ...
jessehouwing

17
Tại sao phải if (doc == null)kiểm tra? Điều này luôn luôn sai, không phải vậy?
avlie

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

Đơn giản và tốt đẹp. Cảm ơn!
Tillito

5
Có nhiều vấn đề - không xử lý các thuộc tính có <hoặc> trong đó và không hoạt động tốt với các thẻ trải rộng trên một dòng trừ khi chạy với RegexOptions.SingleLine.
ChrisF

2
Không, sử dụng "<[^>] *>".
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
Không hoạt động cho nhiều trường hợp bao gồm cả ngắt dòng không trộn.
ChrisF

6

Tôi đã đăng bài này lên các diễn đàn asp.net và dường như đây vẫn là một trong những giải pháp đơn giản nhất. Tôi sẽ không đảm bảo nó nhanh nhất hoặc hiệu quả nhất, nhưng nó khá đáng tin cậy. Trong .NET, bạn có thể sử dụng các đối tượng Điều khiển Web HTML. Tất cả những gì bạn thực sự cần làm là chèn chuỗi của bạn vào một đối tượng HTML tạm thời, chẳng hạn như DIV, sau đó sử dụng 'Nội dung' tích hợp để lấy tất cả văn bản không có trong thẻ. Xem bên dưới để biết ví dụ đơn giản về C #:


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

điều này dường như không hoạt động, tôi đã thử nghiệm nó với SimpleHtml đơn giản = "<b> foo </ b>"; và InnerText có giá trị "<b> foo </ b>" :(
Axarydax

Đừng làm điều này. Giải pháp này tiêm html không mã hóa trực tiếp vào đầu ra. Điều này sẽ giúp bạn mở rộng cho các cuộc tấn công Cross Site Scripting - bạn vừa cho phép bất kỳ ai có thể thay đổi chuỗi html để tiêm bất kỳ html và javascript tùy ý nào vào ứng dụng của bạn!
thuyền

5

Tôi đã viết một phương thức khá nhanh trong c # để đánh bại Regex. Nó được lưu trữ trong một bài viết về CodeProject.

Ưu điểm của nó là, trong số hiệu năng tốt hơn, khả năng thay thế các thực thể HTML được đặt tên và đánh số (những thứ như &amp;amp;&203;) và thay thế khối nhận xét và hơn thế nữa.

Vui lòng đọc bài viết liên quan về CodeProject .

Cảm ơn bạn.


4

Đối với những người bạn không thể sử dụng HtmlAgilityPack, trình đọc XML của .NET là một tùy chọn. Điều này có thể thất bại trên HTML được định dạng tốt mặc dù vậy, luôn luôn thêm một lệnh bắt với regx làm bản sao lưu. Lưu ý điều này KHÔNG nhanh, nhưng nó cung cấp một cơ hội tốt cho bước học cũ thông qua việc gỡ lỗi.

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

Đối với những người đang phàn nàn về giải pháp của Michael Tiptop không hoạt động, đây là cách làm .Net4 +:

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

Tôi đã xem xét các giải pháp dựa trên Regex được đề xuất ở đây và chúng không làm tôi tự tin ngoại trừ trong các trường hợp tầm thường nhất. Một khung góc trong một thuộc tính là tất cả những gì nó cần để phá vỡ, chứ đừng nói đến HTML có dạng mal từ tự nhiên. Và những gì về các thực thể như thế &amp;nào? Nếu bạn muốn chuyển đổi HTML thành văn bản thuần túy, bạn cũng cần giải mã các thực thể.

Vì vậy, tôi đề xuất phương pháp dưới đây.

Sử dụng HtmlAgilityPack , phương thức tiện ích mở rộng này loại bỏ hiệu quả tất cả các thẻ HTML khỏi một đoạn html. Cũng giải mã các thực thể HTML như &amp;. Chỉ trả về các mục văn bản bên trong, với một dòng mới giữa mỗi mục văn bản.

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

Nếu bạn đang thực sự nghiêm trọng, bạn muốn bỏ qua nội dung của các thẻ HTML nào đó quá ( <script>, <style>, <svg>, <head>, <object>tôi suy nghĩ!) Bởi vì họ có thể không chứa nội dung có thể đọc theo nghĩa chúng ta đang theo đuổi. Những gì bạn làm ở đó sẽ phụ thuộc vào hoàn cảnh của bạn và bạn muốn đi bao xa, nhưng sử dụng HtmlAgilityPack, nó sẽ khá tầm thường đối với danh sách trắng hoặc danh sách đen các thẻ được chọn.

Nếu bạn đang hiển thị nội dung trở lại trang HTML, hãy đảm bảo bạn hiểu lỗ hổng XSS & cách ngăn chặn - tức là luôn mã hóa bất kỳ văn bản nào do người dùng nhập trở lại vào trang HTML ( >trở thành &gt;v.v.).


0

Đối với tham số thứ hai, tức là giữ một số thẻ, bạn có thể cần một số mã như thế này bằng cách sử dụng 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();
    }

Giải thích thêm trên trang này: http://nalerskym.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/


0

Bạn cũng có thể làm điều này với AngleSharp , một giải pháp thay thế cho HtmlAgilityPack (không phải HAP là xấu). Nó dễ sử dụng hơn HAP để lấy văn bản ra khỏi nguồn HTML.

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

Bạn có thể xem phần các tính năng chính trong đó họ tạo ra một trường hợp "tốt hơn" so với HAP. Tôi nghĩ rằng đối với hầu hết các phần, nó có thể là quá mức cần thiết cho câu hỏi hiện tại nhưng vẫn, nó là một thay thế thú vị.


-4

Đơn giản chỉ cần sử dụng string.StripHTML();


3
Như @Serpiton chỉ ra, BCL không có phương pháp như vậy. Bạn có thể chỉ ra một triển khai của phương pháp này hoặc cung cấp của riêng bạn?
Sven Grosen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.