使用HtmlAgilityPack将HTML字符串拆分为两部分

时间:2013-11-17 18:57:05

标签: c# html parsing dom html-agility-pack

我正在寻找使用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>

做出类似事情的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

绝对不是。 (注意:这最初是问题上的一个标签 - 现在已经删除了。)我通常不会跳过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. 等等。

答案 1 :(得分:3)

这是我想出的。这会进行拆分并删除发生拆分的元素的“空”元素。

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