Dividere la stringa HTML in due parti con HtmlAgilityPack

c# dom html html-agility-pack parsing

Domanda

Sto cercando il modo migliore per dividere un documento HTML su alcuni tag in C # usando HtmlAgilityPack. Voglio conservare il markup previsto mentre sto facendo lo split. Ecco un esempio.

Se il documento è così:

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

Una volta diviso, dovrebbe assomigliare a questo:

Parte 1

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

Parte 2

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

Quale sarebbe il modo migliore di fare qualcosa del genere?

Risposta accettata

Ecco cosa mi è venuto in mente. Questo fa la divisione e rimuove gli elementi "vuoti" dell'elemento in cui avviene la divisione.

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

Risposta popolare

Sicuramente non dalla . (Nota: questo era in origine un tag sulla domanda - ora rimosso.) Di solito non sono uno a cui saltare il carrozzone di The Pony is Coming, ma questo è un caso in cui le espressioni regolari sarebbero particolarmente negative.

Innanzitutto, scriverei una funzione ricorsiva che rimuove tutti i fratelli di un nodo che seguono quel nodo, chiamiamolo RemoveSiblingsAfter(node) e quindi chiama sé stesso sul suo genitore , in modo che anche tutti i fratelli che seguono il genitore vengano rimossi (e tutti i fratelli seguendo il nonno, e così via). È possibile utilizzare un XPath per trovare i nodi su cui si desidera dividere, ad esempio doc.DocumentNode.SelectNodes("//a[@href='#']") , e chiamare la funzione su quel nodo. Al termine, rimuoverai il nodo di divisione stesso e il gioco è fatto. Ripeteresti questi passaggi per una copia del documento originale, tranne che avresti implementato RemoveSiblingsBefore(node) per rimuovere i fratelli che precedono un nodo.

Nel tuo esempio, RemoveSiblingsBefore agirebbe come segue:

  1. <a href="#"> non ha fratelli, quindi ricorri su genitore, <li> .
  2. <li> ha un fratello precedente- <li>Bullet 1</li> -so rimuovi, e riceva su genitore, <ul> .
  3. <ul> non ha fratelli, quindi recita su genitore, <p> .
  4. <p> ha un fratello precedente- <p>Stuff</p> -so rimuovi, e riceva su genitore, <div> .
  5. e così via.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché