2010-06-29 8 views
16

Mam ciąg html tak:odizolowania wszystkie znaczniki HTML z HTML Agility Paczka

<html><body><p>foo <a href='http://www.example.com'>bar</a> baz</p></body></html> 

Chciałbym rozebrać wszystkie znaczniki HTML tak, że otrzymany ciąg staje:

foo bar baz 

Z innego postu tutaj w SO Mam wymyślić tę funkcję (która używa pakietu Agility Pack Html):

Public Shared Function stripTags(ByVal html As String) As String 
    Dim plain As String = String.Empty 
    Dim htmldoc As New HtmlAgilityPack.HtmlDocument 

    htmldoc.LoadHtml(html) 
    Dim invalidNodes As HtmlAgilityPack.HtmlNodeCollection = htmldoc.DocumentNode.SelectNodes("//html|//body|//p|//a") 

    If Not htmldoc Is Nothing Then 
     For Each node In invalidNodes 
     node.ParentNode.RemoveChild(node, True) 
     Next 
    End If 

    Return htmldoc.DocumentNode.WriteContentTo 
    End Function 

Niestety to nie robi eturn, czego się spodziewam, zamiast tego daje:

bazbarfoo 

Proszę, gdzie mam się pomylić - i czy to jest najlepsze podejście?

Pozdrawiam i szczęśliwe kodowanie!

UPDATE: za odpowiedź poniżej wymyśliłem tej funkcji, może być przydatna do innych:

Public Shared Function stripTags(ByVal html As String) As String 
    Dim htmldoc As New HtmlAgilityPack.HtmlDocument 
    htmldoc.LoadHtml(html.Replace("</p>", "</p>" & New String(Environment.NewLine, 2)).Replace("<br/>", Environment.NewLine)) 
    Return htmldoc.DocumentNode.InnerText 
    End Function 
+0

działa jak czar - dzięki znowu – Muleskinner

Odpowiedz

32

Dlaczego nie po prostu wrócić htmldoc.DocumentNode.InnerText zamiast usunięcie wszystkich węzłów nietekstowe? Powinno dać ci to, czego chcesz.

-6

Można użyć następującego kodu.

public string RemoveHTMLTags(string source) 
{ 
    string expn = "<.*?>"; 
    return Regex.Replace(source, expn, string.Empty); 
} 
+0

Thanks to powinno działać zbyt – Muleskinner

+1

Co o innych rzeczach wewnątrz od < > które nie są tagi html? NA PRZYKŁAD. "John Smith <[email protected]>" ta metoda usunie to. – JDwyer

+3

-1. Parsowanie HTML za pomocą wyrażeń regularnych rzadko jest dobrym pomysłem. Zobacz http://www.codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html – TrueWill

2

Usuwa tagi i właściwości, które nie znajdują się na białej liście.

Public NotInheritable Class HtmlSanitizer 
    Private Sub New() 
    End Sub 
    Private Shared ReadOnly Whitelist As IDictionary(Of String, String()) 
    Private Shared DeletableNodesXpath As New List(Of String)() 

    Shared Sub New() 
     Whitelist = New Dictionary(Of String, String())() From { _ 
      {"a", New() {"href"}}, _ 
      {"strong", Nothing}, _ 
      {"em", Nothing}, _ 
      {"blockquote", Nothing}, _ 
      {"b", Nothing}, _ 
      {"p", Nothing}, _ 
      {"ul", Nothing}, _ 
      {"ol", Nothing}, _ 
      {"li", Nothing}, _ 
      {"div", New() {"align"}}, _ 
      {"strike", Nothing}, _ 
      {"u", Nothing}, _ 
      {"sub", Nothing}, _ 
      {"sup", Nothing}, _ 
      {"table", Nothing}, _ 
      {"tr", Nothing}, _ 
      {"td", Nothing}, _ 
      {"th", Nothing} _ 
     } 
    End Sub 

    Public Shared Function Sanitize(input As String) As String 
     If input.Trim().Length < 1 Then 
      Return String.Empty 
     End If 
     Dim htmlDocument = New HtmlDocument() 

     htmlDocument.LoadHtml(input) 
     SanitizeNode(htmlDocument.DocumentNode) 
     Dim xPath As String = HtmlSanitizer.CreateXPath() 

     Return StripHtml(htmlDocument.DocumentNode.WriteTo().Trim(), xPath) 
    End Function 

    Private Shared Sub SanitizeChildren(parentNode As HtmlNode) 
     For i As Integer = parentNode.ChildNodes.Count - 1 To 0 Step -1 
      SanitizeNode(parentNode.ChildNodes(i)) 
     Next 
    End Sub 

    Private Shared Sub SanitizeNode(node As HtmlNode) 
     If node.NodeType = HtmlNodeType.Element Then 
      If Not Whitelist.ContainsKey(node.Name) Then 
       If Not DeletableNodesXpath.Contains(node.Name) Then 
        'DeletableNodesXpath.Add(node.Name.Replace("?","")); 
        node.Name = "removeableNode" 
        DeletableNodesXpath.Add(node.Name) 
       End If 
       If node.HasChildNodes Then 
        SanitizeChildren(node) 
       End If 

       Return 
      End If 

      If node.HasAttributes Then 
       For i As Integer = node.Attributes.Count - 1 To 0 Step -1 
        Dim currentAttribute As HtmlAttribute = node.Attributes(i) 
        Dim allowedAttributes As String() = Whitelist(node.Name) 
        If allowedAttributes IsNot Nothing Then 
         If Not allowedAttributes.Contains(currentAttribute.Name) Then 
          node.Attributes.Remove(currentAttribute) 
         End If 
        Else 
         node.Attributes.Remove(currentAttribute) 
        End If 
       Next 
      End If 
     End If 

     If node.HasChildNodes Then 
      SanitizeChildren(node) 
     End If 
    End Sub 

    Private Shared Function StripHtml(html As String, xPath As String) As String 
     Dim htmlDoc As New HtmlDocument() 
     htmlDoc.LoadHtml(html) 
     If xPath.Length > 0 Then 
      Dim invalidNodes As HtmlNodeCollection = htmlDoc.DocumentNode.SelectNodes(xPath) 
      For Each node As HtmlNode In invalidNodes 
       node.ParentNode.RemoveChild(node, True) 
      Next 
     End If 
     Return htmlDoc.DocumentNode.WriteContentTo() 


    End Function 

    Private Shared Function CreateXPath() As String 
     Dim _xPath As String = String.Empty 
     For i As Integer = 0 To DeletableNodesXpath.Count - 1 
      If i IsNot DeletableNodesXpath.Count - 1 Then 
       _xPath += String.Format("//{0}|", DeletableNodesXpath(i).ToString()) 
      Else 
       _xPath += String.Format("//{0}", DeletableNodesXpath(i).ToString()) 
      End If 
     Next 
     Return _xPath 
    End Function 
End Class 
+0

W słowniku dla wszystkich pozycji z wyjątkiem pierwszego, jako wartość masz "Nic". Możliwe, że będziesz mógł pominąć użycie mapy i zamiast tego użyć listy. – Zasz

+0

"Lista" będzie prawdopodobnie wolniejsza, choć prawdopodobnie nie będzie wąskim gardłem. Powiedziawszy to, na .Net 3.5+, poleciłbym 'HashSet' zamiast' List' do tego celu. – Brian

+0

Jak wskazuje Brian, wybór struktury danych jest "mało prawdopodobne, aby był wąskim gardłem". W porównaniu do operacji wykonywanych na każdym węźle ContainsKey jest prawdopodobnie pomijalną częścią, nie? –

1

Wydaje się założyć, że ForEach przemierza dokument od początku do końca .. jeśli chcesz się upewnić, że nie jesteś, użyj regularne pętli for. Nie można nawet mieć pewność, że węzły są zabierani w kolejności można się spodziewać z selektora XPath, ale możesz mieć rację z tej okazji ..

regards, Brunis

0

edycja poniżej kilku liniach, następnie dostać się, że chcesz ..

Private Shared Function StripHtml(html As String, xPath As String) As String 
    Dim htmlDoc As New HtmlAgilityPack.HtmlDocument() 
    htmlDoc.LoadHtml(html) 
    If xPath.Length > 0 Then 
     Dim invalidNodes As HtmlNodeCollection = htmlDoc.DocumentNode.SelectNodes(xPath) 

     '------- edit this line ------------------- 
     'For Each node As HtmlNode In invalidNodes 
     'node.ParentNode.RemoveChild(node, True) 
     'Next 
     ' 
     ' result-> bazbarfoo 
     ' 

     '------- modify line ---------------------- 
     For i = invalidNodes.Count - 1 To 0 Step -1 
      Dim Node As HtmlNode = invalidNodes.Item(i) 
      Node.ParentNode.RemoveChild(Node, True) 
     Next 
     ' 
     ' result-> foo bar baz 
     ' 
    End If 
    Return htmlDoc.DocumentNode.WriteContentTo() 


End Function 
Powiązane problemy