我正在寻找使用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>
做出类似事情的最佳方法是什么?
答案 0 :(得分:3)
绝对不是regex。 (注意:这最初是问题上的一个标签 - 现在已经删除了。)我通常不会跳过The Pony即将到来的潮流,但这是一个正则表达式特别糟糕的情况。
首先,我会编写一个递归函数,删除跟随该节点的节点的所有兄弟节点 - 称之为RemoveSiblingsAfter(node)
- 然后调用本身它的父,以便除去父之后的所有兄弟姐妹(以及祖父母后面的所有兄弟姐妹,等等)。您可以使用XPath查找要拆分的节点,例如 doc.DocumentNode.SelectNodes("//a[@href='#']")
,并在该节点上调用该函数。完成后,您将删除拆分节点本身,就是这样。您将重复这些步骤以获取原始文档的副本,除非您实现RemoveSiblingsBefore(node)
以删除位于节点之前的兄弟。
在您的示例中,RemoveSiblingsBefore
的行为如下:
<a href="#">
没有兄弟姐妹,所以请求父母<li>
。<li>
有一个前一个兄弟 - <li>Bullet 1</li>
- 所以删除,并递归父,<ul>
。<ul>
没有兄弟姐妹,所以请求父母<p>
。<p>
有一个前一个兄弟 - <p>Stuff</p>
- 所以删除,并递归父,<div>
。答案 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;
}