Fractionner une chaîne HTML en deux parties avec HtmlAgilityPack

c# dom html html-agility-pack parsing

Question

Je cherche le meilleur moyen de diviser un document HTML sur une balise en C # en utilisant HtmlAgilityPack. Je souhaite conserver le balisage prévu pendant la division. Voici un exemple.

Si le document est comme ceci:

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

Une fois qu'il est divisé, cela devrait ressembler à ceci:

Partie 1

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

Partie 2

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

</p>

Quel serait le meilleur moyen de faire quelque chose comme ça?

Réponse acceptée

Voici ce que je suis venu avec. Cela scinde et supprime les éléments "vides" de l'élément où la scission a lieu

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

Réponse populaire

Certainement pas par . (Remarque: il s'agissait à l'origine d'une balise sur la question - maintenant supprimée.) Je ne suis généralement pas du genre à sauter dans le train en marche, mais c'est un cas dans lequel les expressions régulières seraient particulièrement mauvaises.

Tout d' abord, je voudrais écrire une fonction récursive qui supprime tous les frères et soeurs d'un noeud qui suivent ce nœud-appeler RemoveSiblingsAfter(node) -et puis appelle lui - même à son parent, de sorte que tous les frères et sœurs qui suivent les parents sont aussi supprimés (et tous les frères et sœurs suivant le grand-parent, etc.). Vous pouvez utiliser un XPath pour rechercher le ou les nœuds sur lesquels vous souhaitez scinder, par exemple doc.DocumentNode.SelectNodes("//a[@href='#']") , puis appeler la fonction sur ce nœud. Lorsque vous avez terminé, vous supprimez le nœud de fractionnement lui-même, et c'est tout. Vous devez répéter ces étapes pour une copie du document d'origine, sauf que vous implémentez RemoveSiblingsBefore(node) pour supprimer les frères et soeurs qui précèdent un nœud.

Dans votre exemple, RemoveSiblingsBefore agirait comme suit:

  1. <a href="#"> n'a pas de frères et soeurs, alors appelez le parent, <li> .
  2. <li> a un frère précédent - <li>Bullet 1</li> - supprime donc, et envoie au parent, <ul> .
  3. <ul> n'a pas de frères et soeurs, alors recurse sur le parent, <p> .
  4. <p> a un frère précédent - <p>Stuff</p> - supprime également, et renvoie sur le parent, <div> .
  5. etc.


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi