Я ищу лучший способ разделить документ 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
будет действовать следующим образом:
<a href="#">
не имеет братьев и сестер, поэтому рекурсия родителя, <li>
. <li>
имеет предыдущий sibling- <li>Bullet 1</li>
-so удалять и возвращать родительский элемент <ul>
. <ul>
не имеет братьев и сестер, поэтому recurse на parent, <p>
. <p>
имеет предыдущий sibling- <p>Stuff</p>
-so удалять и рекурсивно на родительском, <div>
.