使用HtmlAgilityPack將HTML字符串拆分為兩部分

c# dom html html-agility-pack parsing

我正在尋找使用HtmlAgilityPack將HTML文檔拆分到C#中的某些標記的最佳方法。我想保留預期的標記,因為我正在進行拆分。這是一個例子。

如果文件是這樣的:

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

熱門答案

絕對不是 。 (注意:這最初是問題上的一個標籤 - 現在被刪除。)我通常不會跳過The Pony即將到來的潮流,但這是一個正則表達式特別糟糕的案例。

首先,我會寫一個遞歸函數,消除遵循一個節點的所有兄弟節點稱之為RemoveSiblingsAfter(node) -然後自稱母公司 ,因此跟在父母兄弟姐妹都被刪除,以及(和所有的兄弟姐妹跟隨祖父母,等等)。您可以使用XPath查找要拆分的節點, 例如 doc.DocumentNode.SelectNodes("//a[@href='#']") ,並在該節點上調用該函數。完成後,您將刪除拆分節點本身,就是這樣。您將重複這些步驟以獲取原始文檔的副本,除非您實現RemoveSiblingsBefore(node)以刪除節點之前的兄弟節點。

在您的示例中, RemoveSiblingsBefore將執行如下操作:

  1. <a href="#">沒有兄弟姐妹,所以要求父母, <li>
  2. <li>有一個前面的兄弟 - <li>Bullet 1</li>刪除,並遞歸父, <ul>
  3. <ul>沒有兄弟姐妹,所以遞歸父母, <p>
  4. <p>有一個前面的兄弟 - <p>Stuff</p>刪除,並遞歸父, <div>
  5. 等等。


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow