HtmlAgilityPack : html 요소를 클래스와 함께 하나의 태그로 결합하려면 어떻게해야합니까?

html-agility-pack vb.net

문제

문제 : HtmlAgilityPack을 사용하여 일부 HTML 요소를 검사하고 태그 이름을 결합해야합니다. 부모로부터 자식에게 각 태그를 추출 할 수 있습니까? "strikeUEmStrong"이라는 이름의 클래스가있는 범위로 바꿉니다. 또한 이름은 HTML 요소를 기반으로 변경됩니다.

수업 명칭의 순서는 사실 문제이며, 시행 착오를 통해 깨달았습니다. 모든 요소를 ​​가져 와서 결합 할 수있는 한. 다양한 형식의 서식이있는 여러 텍스트 노드를 가질 가능성이 매우 높습니다.

이것은 여러 단락에 영향을 미칩니다.

예를 들어,이 html 코드가있는 경우 :

<p>
<strike><u><em><strong>four styles</strong></em></u></strike></p>

어떻게 변환합니까?

<p>
<span class="strikeUEmStrong">four styles</span></p>

이 유형의 코드도 사용할 수 있습니다.

<p>
    <strike><u><em><strong>four styles</strong></em></u></strike>&nbsp; <strike><u><em>three styles</em></u></strike></p>
<p>
    <em><strong>two styles</strong></em></p>

출력은 다음과 같아야합니다.

<p>
<span class="strikeUEmStrong">four styles</span>&nbsp; <span class="strikeUEm">three styles<span></p><p><span class="emStrong">two styles<span></p>

원기:

'Retrive the class name of each format node
Function GetClassName(ByVal n As HtmlNode) As String
    Dim ret As String = String.Empty

    If (n.Name <> "#text") And (n.Name <> "p") Then
        ret = n.Name + " "
    End If

    'Get the next node
    For Each child As HtmlNode In n.ChildNodes
        ret &= GetClassName(child)
    Next

    Return ret
End Function

'Create a list of class names
Function GetClassNameList(ByVal classNameList As String) As List(Of String)
    Dim ret As New List(Of String)
    Dim classArr() As String = classNameList.Split(" ")

    For Each className As String In classArr
        ret.Add(className)
    Next

    Return ret
End Function

'Sort a list of class names and return a merged class string
Function GetSortedClassNameString(ByVal classList As List(Of String)) As String

    Dim sortedMergedClass As String = String.Empty

    classList.Sort()

    For Each className As String In classList
        sortedMergedClass &= className
    Next

    Return sortedMergedClass
End Function

'Lets point to the body node
Dim bodyNode As HtmlNode = htmlDoc.DocumentNode.SelectSingleNode("//body")

'Lets create some generic nodes
Dim currPNode As HtmlNode

Dim formatNodes As HtmlNodeCollection

Dim text As String = String.Empty
Dim textSize As Integer = 0

'Make sure the editor has something in it
If editorText <> "" Then

   'Send the text from the editor to the body node
    If bodyNode IsNot Nothing Then
       bodyNode.InnerHtml = editorText
    End If

    Dim pNode = bodyNode.SelectNodes("//p")

    Dim span As HtmlNode = htmlDoc.CreateElement("span")
    Dim tmpBody As HtmlNode = htmlDoc.CreateElement("body")
    Dim textNode As HtmlNode = htmlDoc.CreateTextNode

    Dim pCount As Integer = bodyNode.SelectNodes("//body/p").Count - 1

    For childCountP As Integer = 0 To pCount

        Dim paragraph = HtmlNode.CreateNode(htmlDoc.CreateElement("p").WriteTo)

        'Which paragraph I am at.
        currPNode = pNode.Item(childCountP)

        'For this paragraph get me the collection of html nodes
        formatNodes = currPNode.ChildNodes

        'Count how many Format nodes we have in a paragraph
        Dim formatCount As Integer = currPNode.ChildNodes.Count - 1

       'Go through each node and examine the elements. 
       'Then look at the markup to create classes and then group them under one span
       For child As Integer = 0 To formatCount

           'Iterate through the formateNodes, strike, em, strong, etc.
           Dim currFormatNode = HtmlNode.CreateNode(formatNodes(child).WriteTo)

           'TODO: Handle nested images and links? How do we know what to rip out?

           'First check for format nodes
           'Note, we can't let it use everything because it will change nested elements as well. I.E. span within span.
           If (currFormatNode.Name <> "#text") And (currFormatNode.Name = "strike") Or (currFormatNode.Name = "em") _
               Or (currFormatNode.Name = "strong") Or (currFormatNode.Name = "u") Or (currFormatNode.Name = "sub") _
               Or (currFormatNode.Name = "sup") Or (currFormatNode.Name = "b") Then

              'strip all tags, just take the inner text
              span.InnerHtml = currFormatNode.InnerText

              'Create a text node with text from the lowest node
              textNode = htmlDoc.CreateTextNode(span.InnerText)

              'Recursively go through the format nodes
              'Create a list from the string
              'Then sort the list and return a string
              'Appending the class to the span
               span.SetAttributeValue("class", GetSortedClassNameString(GetClassNameList(GetClassName(currFormatNode).Trim())))

              'Attach the span before the current format node
              currFormatNode.ParentNode.InsertBefore(span, currFormatNode)

             'Remove the formatted children leaving the above node
             currFormatNode.ParentNode.ChildNodes.Remove(currFormatNode)

             'We need to build a paragraph here
             paragraph.InnerHtml &= span.OuterHtml

             'Lets output something for debugging
             childNodesTxt.InnerText &= span.OuterHtml

             Else 'handle #text and other nodes seperately
                  'We need to build a paragraph here
                  paragraph.InnerHtml &= span.OuterHtml
                  textNode = htmlDoc.CreateTextNode(currFormatNode.InnerHtml)

                  'Lets output something for debugging
                  childNodesTxt.InnerText &= textNode.OuterHtml
             End If

        Next
        'End of formats

        'Start adding the new paragraph's to the body node
        tmpBody.AppendChild(paragraph)
     Next
     'End of paragraphs

    'Clean out body first and replace with new elements
    htmlDoc.DocumentNode.SelectSingleNode("//body").Remove()

    'Update our body
    htmlDoc.DocumentNode.SelectSingleNode("//html").AppendChild(tmpBody)

 End If

 htmlDoc.Save(Server.MapPath("html\editor.html"))
 End If

산출:

<span class="strikeuemstrong">four styles</span>

마지막으로 올바른 출력을 얻으려면 주문 문제를 해결해야합니다. 도와 주셔서 감사합니다.

수락 된 답변

이것은 대답 할 수있는 직접적인 질문이 아닙니다. 이 작업을 수행하는 알고리즘을 작성하는 방법을 설명하고 의사 코드를 포함시켜 도움을줍니다.

  1. 내 부모 태그를 얻을거야. 모든 "p"태그에 대해이 작업을 수행한다고 가정합니다.
  2. 내 자식 태그를 반복하고 태그 이름을 가져 와서 클래스 이름에 추가합니다.
  3. 추가 된 태그 이름을 얻을 때까지 반복적으로 자식을 반복합니다.

의사 코드. 이것을 타이핑 할 때 오타를 용서하십시오.

public string GetClassName(Node n)
{
var ret = n.TagName;

foreach(var child in n.ChildNodes)
{
ret += GetClassName(child);
}

return ret;
}


foreach(var p in paragraphs)
{
foreach(var child in p.ChildNodes)
{
 var span = new Span();
 span.InnerText = child.InnerText; // strip all tags, just take the inner text

span.ClassName = GetClassName(child);

child.ReplaceWith(span); // note: if you do this with a FOREACH and not a for loop, it'll blow up C# for modifying the collection while iterating.  Use for loops. if you're going to do "active" replacement like in this pseudo code
}
}

더 많은 컨텍스트를 얻으면 내 대답을 수정 해 드리겠습니다. 제 제안을 수정해야 할 경우, 제가 제안하고있는 것을 검토하고 더 많은 맥락으로 의견을 말하십시오. 그렇지 않다면, 나는 이것이 당신에게 필요한 것을 얻을 수 있기를 바랍니다 :)



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.