HTML 문자열을 HtmlAgilityPack으로 두 부분으로 나누기

c# dom html html-agility-pack parsing

문제

HtmlAgilityPack 사용하여 C #에서 일부 태그 위로 HTML 문서를 분할하는 가장 좋은 방법을 찾고 있어요. 분할을 수행함에 따라 의도 한 마크 업을 유지하려고합니다. 다음은 그 예입니다.

문서가 다음과 같은 경우 :

<p>
<div>
    <p>
        Stuff
    </p>
    <p>
        <ul>
            <li>Bullet 1</li>
            <li><a href="#">link</a></li>
            <li>Bullet 3</li>
        </ul>
    </p>
            <span>Footer</span>
</div>
</p>

분할되면 다음과 같이 보입니다.

1 부

<p>
<div>
    <p>
        Stuff
    </p>
    <p>
        <ul>
            <li>Bullet 1</li>
        </ul>
    </p>
</div>
</p>

2 부

<p>
<div>
    <p>
        <ul>
            <li>Bullet 3</li>
        </ul>
    </p>
            <span>Footer</span>
</div>

</p>

그런 일을하는 가장 좋은 방법은 무엇일까요?

수락 된 답변

여기에 내가 생각해 낸 것이있다. 이것은 분할을 수행하고 분할이 발생하는 요소의 "비어있는"요소를 제거합니다.

    private static void SplitDocument()
    {
        var doc = new HtmlDocument();
        doc.Load("HtmlDoc.html");
        var links = doc.DocumentNode.SelectNodes("//a[@href]");
        var firstPart = GetFirstPart(doc.DocumentNode, links[0]).DocumentNode.InnerHtml;
        var secondPart = GetSecondPart(links[0]).DocumentNode.InnerHtml;
    }

    private static HtmlDocument GetFirstPart(HtmlNode currNode, HtmlNode link)
    {
        var nodeStack = new Stack<Tuple<HtmlNode, HtmlNode>>();        
        var newDoc = new HtmlDocument();
        var parent = newDoc.DocumentNode;

        nodeStack.Push(new Tuple<HtmlNode, HtmlNode>(currNode, parent));

        while (nodeStack.Count > 0)
        {
            var curr = nodeStack.Pop();
            var copyNode = curr.Item1.CloneNode(false);
            curr.Item2.AppendChild(copyNode);

            if (curr.Item1 == link)
            {
                var nodeToRemove = NodeAndEmptyAncestors(copyNode);
                nodeToRemove.ParentNode.RemoveChild(nodeToRemove);
                break;
            }

            for (var i = curr.Item1.ChildNodes.Count - 1; i >= 0; i--)
            {
                nodeStack.Push(new Tuple<HtmlNode, HtmlNode>(curr.Item1.ChildNodes[i], copyNode));
            }
        }

        return newDoc;
    }

    private static HtmlDocument GetSecondPart(HtmlNode link)
    {
        var nodeStack = new Stack<HtmlNode>();
        var newDoc = new HtmlDocument();

        var currNode = link;
        while (currNode.ParentNode != null)
        {
            currNode = currNode.ParentNode;
            nodeStack.Push(currNode.CloneNode(false));
        }

        var parent = newDoc.DocumentNode;
        while (nodeStack.Count > 0)
        {
            var node = nodeStack.Pop();
            parent.AppendChild(node);
            parent = node;
        }

        var newLink = link.CloneNode(false);
        parent.AppendChild(newLink);

        currNode = link;
        var newParent = newLink.ParentNode;

        while (currNode.ParentNode != null)
        {
            var foundNode = false;
            foreach (var child in currNode.ParentNode.ChildNodes)
            {
                if (foundNode) newParent.AppendChild(child.Clone());
                if (child == currNode) foundNode = true;
            }

            currNode = currNode.ParentNode;
            newParent = newParent.ParentNode;
        }

        var nodeToRemove = NodeAndEmptyAncestors(newLink);
        nodeToRemove.ParentNode.RemoveChild(nodeToRemove);

        return newDoc;
    }

    private static HtmlNode NodeAndEmptyAncestors(HtmlNode node)
    {
        var currNode = node;
        while (currNode.ParentNode != null && currNode.ParentNode.ChildNodes.Count == 1)
        {
            currNode = currNode.ParentNode;
        }

        return currNode;
    }

인기 답변

확실히 아닙니다. (참고 : 원래이 질문에 대한 태그였습니다. 이제 제거되었습니다.) 나는 보통 포니가 등장 할 때가 아니지만 정규 표현식이 특히 나쁜 경우 중 하나입니다.

먼저 노드에 이어 노드의 모든 형제를 제거하는 RemoveSiblingsAfter(node) 라고 부르는 모든 형제 노드를 제거하고 그 부모 RemoveSiblingsAfter(node) 자신 을 호출하여 부모 다음의 모든 형제 노드를 제거하는 재귀 함수를 작성합니다. 조부모를 따르는 등). XPath를 사용하여 분할하려는 노드를 찾을 수 있습니다 ( 예 : doc.DocumentNode.SelectNodes("//a[@href='#']") . 해당 노드에서 함수를 호출합니다. 끝나면 분할 노드 자체를 제거하면됩니다. RemoveSiblingsBefore(node) 를 구현하여 노드 앞에 있는 형제를 제거하는 것을 제외하고는 원본 문서의 복사본에 RemoveSiblingsBefore(node) 단계를 반복합니다.

귀하의 예에서 RemoveSiblingsBefore 는 다음과 같이 작동합니다.

  1. <a href="#"> 에는 형제가 없으므로 부모에게 재발합니다. <li>
  2. <li> 이전 형제가 있습니다. <li>Bullet 1</li> - 부모를 제거하고 재발생합니다. <ul> .
  3. <ul> 에는 형제가 없으므로 부모에게 재귀가 발생합니다 ( <p> .
  4. <p> 앞에는 형제가 있습니다 ( <p>Stuff</p> <div> . 따라서 부모를 <div> 제거하고 재발행합니다.
  5. 등등.



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