ฉันจะดึงแท็ก HTML ออกจากสตริงใน ASP.NET ได้อย่างไร


123

การใช้ ASP.NET ฉันจะดึงแท็ก HTML ออกจากสตริงที่กำหนดอย่างน่าเชื่อถือได้อย่างไร (เช่นไม่ใช้ regex) ฉันกำลังมองหาสิ่งที่ต้องการของ strip_tagsPHP

ตัวอย่าง:

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

เอาท์พุท:

"สวัสดี"

ฉันพยายามที่จะไม่คิดค้นล้อใหม่ แต่ยังไม่พบสิ่งที่ตรงกับความต้องการของฉัน


ฉันจะนึกภาพว่า PHP strip_tags ใช้ regex อยู่เบื้องหลัง!
stevehipwell

10
@ แดเนียล: เพราะ regex นั้นแย่มากโดยเฉพาะอย่างยิ่งถ้าคุณทำรัง
Joel Coehoorn

อืมดูเหมือนว่า Strip_Tags ของ PHP จะไม่น่าเชื่อถือเป็นพิเศษไม่ว่าจะเป็นบันทึกอย่างเป็นทางการและความคิดเห็น: uk.php.net/strip_tags
Zhaph - Ben Duguid

คำตอบ:


112

หากเป็นเพียงการลอกแท็ก HTML ทั้งหมดออกจากสตริงสิ่งนี้จะทำงานได้อย่างน่าเชื่อถือกับ regex เช่นกัน แทนที่:

<[^>]*(>|$)

ด้วยสตริงว่างทั่วโลก อย่าลืมทำให้สตริงเป็นปกติในภายหลังโดยแทนที่:

[\s\r\n]+

ด้วยช่องว่างเดียวและตัดแต่งผลลัพธ์ สามารถเลือกที่จะแทนที่เอนทิตีอักขระ HTML กลับเป็นอักขระจริง

หมายเหตุ :

  1. มีข้อ จำกัด : HTML และ XML อนุญาต>ในค่าแอตทริบิวต์ โซลูชันนี้จะคืนค่ามาร์กอัปที่เสียเมื่อพบค่าดังกล่าว
  2. วิธีแก้ปัญหานี้มีความปลอดภัยในทางเทคนิคเช่น: ผลลัพธ์จะไม่มีสิ่งใดที่สามารถใช้ในการทำสคริปต์ข้ามไซต์หรือทำลายเค้าโครงหน้าได้ มันไม่สะอาดมาก
  3. เช่นเดียวกับ HTML และ regex:
    ใช้ตัวแยกวิเคราะห์ที่เหมาะสมหากคุณต้องทำให้ถูกต้องในทุกสถานการณ์

52
แม้ว่าจะไม่ได้ร้องขอ แต่ฉันคิดว่าผู้อ่านจำนวนมากจะต้องการตัดการเข้ารหัส HTM ด้วยเช่น&quote;กัน ฉันรวมเข้ากับWebUtility.HtmlDecodeสิ่งนั้น (ซึ่งจะไม่ลบแท็ก) ใช้หลังจากการลบแท็กเนื่องจากอาจเขียนซ้ำ&gt;และ &lt;เช่นWebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious

@YahooSerious ขอบคุณที่ยกตัวอย่าง ใช้งานได้ดี ขอบคุณ.
SearchForKnowledge

Html Agility Pack เป็นวิธีที่จะไปฉันใช้วิธีนี้ในรูปแบบเว็บเพื่อดึงหน้าเว็บทั้งหมดออกเพื่อใช้เนื้อหา!
Bojangles

3
@YahooSerious สิ่งนี้จะอนุญาตให้ใช้เวกเตอร์ XSS ในอย่างไรก็ตาม & gt; สคริปต์ & lt; การแจ้งเตือน ( "XXS"); & gt; / script & lt; จะไม่ถูกล้างโดย regex แต่แปลงโดย HtmlDecode เป็น <script> alert ("XXS"); </ script>

1
@Heather จุดที่ดีมาก การลอกแท็ก HTML จะต้องทำอีกครั้งหลังจากการถอดรหัสเอนทิตี
Tomalak

76

ไปดาวน์โหลด HTMLAgilityPack เลย! ;) ดาวน์โหลด LInk

สิ่งนี้ช่วยให้คุณโหลดและแยกวิเคราะห์ HTML จากนั้นคุณสามารถนำทาง DOM และแยกค่าภายในของแอตทริบิวต์ทั้งหมด อย่างจริงจังจะใช้รหัสสูงสุดประมาณ 10 บรรทัด เป็นหนึ่งในไลบรารี. net ฟรีที่ยิ่งใหญ่ที่สุด

นี่คือตัวอย่าง:

            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
คุณยังสามารถค้นหาทุกtext()โหนดตัดแต่งเนื้อหาและสตริงเข้าร่วมกับผู้ที่มีช่องว่าง IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing

หรือใช้ doc.DocumentNode.InnerText แม้ว่าจะมีปัญหาบางอย่างเกี่ยวกับช่องว่างการจัดการดูเหมือนว่า ...
jessehouwing

17
ทำไมต้องif (doc == null)เช็ค? นี่เป็นเท็จเสมอไม่ใช่เหรอ?
avesse

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

เรียบง่ายและดูดี ขอบคุณ!
Tillito

5
มีหลายประเด็น - ไม่ได้จัดการกับคุณลักษณะที่มี <หรือ> RegexOptions.SingleLineในพวกเขาและไม่ได้ทำดีกับแท็กที่ช่วงมากกว่าหนึ่งบรรทัดเว้นแต่การทำงานกับ
ChrisF

2
Noooo ให้ใช้ "<[^>] *>"
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
ใช้ไม่ได้กับหลาย ๆ กรณีรวมถึงการแตกไลน์ที่ไม่ใช่ Unix
ChrisF

6

ฉันโพสต์สิ่งนี้ไว้ในฟอรัม asp.net แล้วและดูเหมือนว่าจะเป็นวิธีแก้ปัญหาที่ง่ายที่สุดวิธีหนึ่ง ฉันไม่รับประกันว่ามันเร็วที่สุดหรือมีประสิทธิภาพมากที่สุด แต่ก็ค่อนข้างน่าเชื่อถือ ใน. NET คุณสามารถใช้อ็อบเจ็กต์ HTML Web Control ได้เอง สิ่งที่คุณต้องทำจริงๆคือใส่สตริงของคุณลงในวัตถุ HTML ชั่วคราวเช่น DIV จากนั้นใช้ 'InnerText' ในตัวเพื่อดึงข้อความทั้งหมดที่ไม่มีอยู่ในแท็ก ดูตัวอย่าง C # อย่างง่ายด้านล่าง:


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

ดูเหมือนจะไม่ได้ผลฉันทดสอบด้วย InnerHtml = "<b> foo </b>" อย่างง่าย; และ InnerText มีค่า "<b> foo </b>" :(
Axarydax

อย่าทำแบบนี้ โซลูชันนี้ฉีด html ที่ไม่ได้เข้ารหัสลงในเอาต์พุตโดยตรง สิ่งนี้จะทำให้คุณเปิดกว้างต่อการโจมตี Cross Site Scripting - คุณเพิ่งอนุญาตให้ทุกคนที่สามารถเปลี่ยนสตริง html เพื่อฉีด html และ javascript ใด ๆ ลงในแอปพลิเคชันของคุณโดยพลการ!
saille

5

ฉันได้เขียนวิธีการที่ค่อนข้างรวดเร็วใน c # ซึ่งเอาชนะนรกจาก Regex โฮสต์อยู่ในบทความเกี่ยวกับ CodeProject

ข้อดีของมันคือประสิทธิภาพที่ดีกว่าคือความสามารถในการแทนที่เอนทิตี HTML ที่มีชื่อและลำดับเลข (สิ่งที่ชอบ&amp;amp;และ&203;) และการแทนที่บล็อกความคิดเห็นและอื่น ๆ

โปรดอ่านบทความที่เกี่ยวข้องบน CodeProject

ขอบคุณ.


4

สำหรับผู้ที่ไม่สามารถใช้ HtmlAgilityPack โปรแกรมอ่าน XML ของ. NETs เป็นตัวเลือก สิ่งนี้อาจล้มเหลวใน HTML ที่มีการจัดรูปแบบอย่างดีแม้ว่าจะต้องเพิ่ม catch กับ regx เป็นข้อมูลสำรองเสมอ โปรดทราบว่านี่ไม่ใช่เรื่องเร็ว แต่เป็นโอกาสที่ดีสำหรับการก้าวไปสู่โรงเรียนเก่าผ่านการแก้ไขจุดบกพร่อง

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


1

สำหรับผู้ที่กำลังดูว่าโซลูชันของ Michael Tiptop ไม่ทำงานนี่คือวิธีการ. 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;
    }
}


0

ฉันได้ดูโซลูชันที่ใช้ Regex ที่แนะนำที่นี่แล้วและพวกเขาไม่ได้เติมเต็มความมั่นใจให้ฉันยกเว้นในกรณีที่ไม่สำคัญที่สุด วงเล็บมุมในแอตทริบิวต์คือทั้งหมดที่ต้องใช้ในการทำลายไม่ว่าจะเป็น HTML ที่ปลอมแปลงผิดพลาดจากป่า แล้วเอนทิตี&amp;ล่ะ? หากคุณต้องการแปลง HTML เป็นข้อความธรรมดาคุณต้องถอดรหัสเอนทิตีด้วย

ผมจึงขอเสนอวิธีด้านล่างนี้

การใช้HtmlAgilityPackวิธีส่วนขยายนี้จะดึงแท็ก HTML ทั้งหมดออกจากส่วน html ได้อย่างมีประสิทธิภาพ ถอดรหัสเอนทิตี HTML เช่น&amp;. ส่งคืนเฉพาะรายการข้อความภายในโดยมีบรรทัดใหม่ระหว่างแต่ละรายการข้อความ

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

หากคุณจริงจังจริงๆคุณต้องการที่จะไม่สนใจเนื้อหาของแท็ก HTML บางเกินไป ( <script>, <style>, <svg>, <head>, <object>มาใจ!) เพราะพวกเขาอาจจะไม่ได้มีเนื้อหาที่อ่านได้ในความรู้สึกที่เรามีความหลัง สิ่งที่คุณทำจะขึ้นอยู่กับสถานการณ์ของคุณและว่าคุณต้องการไปไกลแค่ไหน แต่การใช้ HtmlAgilityPack จะเป็นเรื่องเล็กน้อยสำหรับแท็กที่เลือกในรายการที่อนุญาตหรือบัญชีดำ

หากคุณกำลังแสดงเนื้อหากลับไปที่หน้า HTML ตรวจสอบให้แน่ใจว่าคุณเข้าใจช่องโหว่ของ XSS และวิธีการป้องกันนั่นคือเข้ารหัสข้อความที่ผู้ใช้ป้อนซึ่งจะแสดงผลกลับไปที่หน้า HTML เสมอ ( >กลายเป็น&gt;ฯลฯ )


0

สำหรับพารามิเตอร์ที่สองเช่นเก็บแท็กไว้คุณอาจต้องใช้โค้ดเช่นนี้โดยใช้ 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();
    }

คำอธิบายเพิ่มเติมในหน้านี้: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/


0

คุณสามารถทำได้ด้วยAngleSharpซึ่งเป็นอีกทางเลือกหนึ่งของ HtmlAgilityPack (ไม่ใช่ว่า HAP ไม่ดี) ใช้งานง่ายกว่า HAP เพื่อดึงข้อความออกจากซอร์ส HTML

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

คุณสามารถดูส่วนคุณสมบัติหลักที่พวกเขาสร้างกรณีที่ "ดีกว่า" มากกว่า HAP ฉันคิดว่าส่วนใหญ่แล้วมันอาจจะมากเกินไปสำหรับคำถามปัจจุบัน แต่ก็ยังเป็นทางเลือกที่น่าสนใจ


-4

เพียงแค่ใช้ string.StripHTML();


3
ดังที่ @Serpiton ชี้ให้เห็นว่าไม่มีวิธีการดังกล่าวใน BCL คุณสามารถชี้ไปที่การใช้งานวิธีนี้หรือระบุของคุณเองได้หรือไม่?
Sven Grosen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.