Разделение HTML-строки на две части с помощью HtmlAgilityPack

c# dom html html-agility-pack parsing

Вопрос

Я ищу лучший способ разделить документ HTML над некоторым тегом на C #, используя HtmlAgilityPack. Я хочу сохранить предполагаемую разметку, поскольку я делаю раскол. Вот пример.

Если документ выглядит следующим образом:

<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) - и затем вызывает себя на своем родителе , так что все братья и сестры, следующие за родителем , также удаляются (и все братья и сестры после дедушки и бабушки и т. д.). Вы можете использовать XPath для поиска узлов, на которых вы хотите разбить, например doc.DocumentNode.SelectNodes("//a[@href='#']") и вызвать функцию на этом узле. Когда закончите, вы удалите сам узел расщепления, и все. Вы повторите эти шаги для копии исходного документа, за исключением того, что вы должны реализовать RemoveSiblingsBefore(node) чтобы удалить братьев и сестер, предшествующих узлу.

В вашем примере, RemoveSiblingsBefore будет действовать следующим образом:

  1. <a href="#"> не имеет братьев и сестер, поэтому рекурсия родителя, <li> .
  2. <li> имеет предыдущий sibling- <li>Bullet 1</li> -so удалять и возвращать родительский элемент <ul> .
  3. <ul> не имеет братьев и сестер, поэтому recurse на parent, <p> .
  4. <p> имеет предыдущий sibling- <p>Stuff</p> -so удалять и рекурсивно на родительском, <div> .
  5. и так далее.


Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow