【问题标题】:Html Agility Pack - Remove element, but not innerHtmlHtml Agility Pack - 删除元素,但不删除 innerHtml
【发布时间】:2012-08-23 13:22:25
【问题描述】:

我可以像这样通过 note.Remove() 轻松删除元素:

HtmlDocument html = new HtmlDocument();

html.Load(Server.MapPath(@"~\Site\themes\default\index.cshtml"));

foreach (var item in html.DocumentNode.SelectNodes("//removeMe"))
{
    item.Remove();
}

但这也会删除 innerHtml。 如果我只想删除标签,并保留 innerHtml 怎么办?

例子:

<ul>
    <removeMe>
        <li>
            <a href="#">Keep me</a>
        </li>
    </removeMe>
</ul>

任何帮助将不胜感激:)

【问题讨论】:

  • 找到removeMe节点的父节点,将removeMe节点的innerHtml追加到父节点的innerHtml中然后去掉? :-)
  • 想过,但是如果父节点包含5个嵌套节点,并且removeMe是第3个,那么如果我将removeMe的innerHtml附加到父节点,位置不再相同。
  • 好吧,也许你实际上可以用它的innerHtml替换removeMe节点,或者在前一个节点之后插入,没有太多经验替换 HTML使用HTMLAP,但是浏览并且遍历 DOM 树真的很容易。
  • 另一种解决方案是在remove me上使用InsertAfter,插入innerHtml,然后removeremoveMe,但我不知道如何正确使用insertAfter。
  • @CodeCaster,现在尝试一下是个好主意。编辑,没有replace方法,只有replaceChild方法。

标签: c# html html-agility-pack


【解决方案1】:
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);

var node = doc.DocumentNode.SelectSingleNode("//removeme");
node.ParentNode.RemoveChild(node, true);

【讨论】:

  • "bool KeepGrandChildren",这绝对是最好的解决方案,谢谢!
  • 很好,它对你有用吗?我总是得到那个异常:在集合中找不到节点“” - 我的测试html: - 当我用它测试它时:"”然后我得到意外错误。
  • 这很奇怪,它对我有用,即使没有父节点。
  • 如果 removeme 标签中有文本,文本也会被删除。例如 text

    more text

    将变为

    more text

【解决方案2】:

这应该可行:

foreach (var item in doc.DocumentNode.SelectNodes("//removeMe"))
{
    if (item.PreviousSibling == null)
    {
        //First element -> so add it at beginning of the parent's innerhtml
        item.ParentNode.InnerHtml = item.InnerHtml + item.ParentNode.InnerHtml;
    }
    else
    {
        //There is an element before itemToRemove -> add the innerhtml after the previous item
        foreach(HtmlNode node in item.ChildNodes){
            item.PreviousSibling.ParentNode.InsertAfter(node, item.PreviousSibling);
        }
    }
    item.Remove();
}

【讨论】:

  • 这对我的测试用例不起作用。 :) 我不知道你为什么要做 HTML 连接部分。
  • 如果有先前的兄弟,这将产生一个排序错误;您必须向后遍历 childnodes 集合。
【解决方案3】:

bool KeepGrandChildren 实现存在问题,对于那些可能包含他们试图删除的元素的文本的人。如果 removeme 标记中包含文本,则该文本也将被删除。例如&lt;removeme&gt;text&lt;p&gt;more text&lt;/p&gt;&lt;/removeme&gt; 将变为&lt;p&gt;more text&lt;/p&gt;

试试这个:

private static void RemoveElementKeepText(HtmlNode node)
    {
        //node.ParentNode.RemoveChild(node, true);
        HtmlNode parent = node.ParentNode;
        HtmlNode prev = node.PreviousSibling;
        HtmlNode next = node.NextSibling;

        foreach (HtmlNode child in node.ChildNodes)
        {
            if (prev != null)
                parent.InsertAfter(child, prev);
            else if (next != null)
                parent.InsertBefore(child, next);
            else
                parent.AppendChild(child);

        }
        node.Remove();
    }

【讨论】:

  • 感谢您的解决方案,但正如所写,这仍然会产生与发布代码中相同的排序错误!在您的 foreach 循环中,您需要向后迭代以使子项按顺序出现。
  • 我正在使用的版本没有颠倒顺序,所以我不确定你指的是什么。
  • 抱歉,我看到您正在解决另一个错误。这是我正在谈论的那个:htmlagilitypack.codeplex.com/discussions/79587 如果您查看HtmlNode.RemoveChildren() 的源代码,您会看到您的代码包含相同的错误;您需要以相反的顺序处理和插入孙子,以便它们正确出现。
【解决方案4】:

有一个简单的方法:

 element.InnerHtml = element.InnerHtml.Replace("<br>", "{1}"); 
 var innerTextWithBR = element.InnerText.Replace("{1}", "<br>");

【讨论】:

    【解决方案5】:

    加上我的两分钱,因为这些方法都没有处理我想要的(删除一组给定的标签,如 pdiv 并在保留内部标签的同时正确处理嵌套)。

    这是我想出的并通过了所有单元测试的方法,我认为大多数情况下我需要处理:

    var htmlDoc = new HtmlDocument();
    
    // load html
    htmlDoc.LoadHtml(html);
    
    var tags = (from tag in htmlDoc.DocumentNode.Descendants()
               where tagNames.Contains(tag.Name)
               select tag).Reverse();
    
    // find formatting tags
    foreach (var item in tags)
    {
        if (item.PreviousSibling == null)
        {
            // Prepend children to parent node in reverse order
            foreach (HtmlNode node in item.ChildNodes.Reverse())
            {
                item.ParentNode.PrependChild(node);
            }                        
        }
        else
        {
            // Insert children after previous sibling
            foreach (HtmlNode node in item.ChildNodes)
            {
                item.ParentNode.InsertAfter(node, item.PreviousSibling);
            }
        }
    
        // remove from tree
        item.Remove();
    }
    
    // return transformed doc
    html = htmlDoc.DocumentNode.WriteContentTo().Trim();
    

    以下是我曾经测试过的案例:

    [TestMethod]
    public void StripTags_CanStripSingleTag()
    {
        var input = "<p>tag</p>";
        var expected = "tag";
        var actual = HtmlUtilities.StripTags(input, "p");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripNestedTag()
    {
        var input = "<p>tag <p>inner</p></p>";
        var expected = "tag inner";
        var actual = HtmlUtilities.StripTags(input, "p");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripTwoTopLevelTags()
    {
        var input = "<p>tag</p> <div>block</div>";
        var expected = "tag block";
        var actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripMultipleNestedTags_2LevelsDeep()
    {
        var input = "<p>tag <div>inner</div></p>";
        var expected = "tag inner";
        var actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripMultipleNestedTags_3LevelsDeep()
    {
        var input = "<p>tag <div>inner <p>superinner</p></div></p>";
        var expected = "tag inner superinner";
        var actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripTwoTopLevelMultipleNestedTags_3LevelsDeep()
    {
        var input = "<p>tag <div>inner <p>superinner</p></div></p> <div><p>inner</p> toplevel</div>";
        var expected = "tag inner superinner inner toplevel";
        var actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_IgnoresTagsThatArentSpecified()
    {
        var input = "<p>tag <div>inner <a>superinner</a></div></p>";
        var expected = "tag inner <a>superinner</a>";
        var actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    
        input = "<wrapper><p>tag <div>inner</div></p></wrapper>";
        expected = "<wrapper>tag inner</wrapper>";
        actual = HtmlUtilities.StripTags(input, "p", "div");
    
        Assert.AreEqual(expected, actual);
    }
    
    [TestMethod]
    public void StripTags_CanStripSelfClosingAndUnclosedTagsLikeBr()
    {
        var input = "<p>tag</p><br><br/>";
        var expected = "tag";
        var actual = HtmlUtilities.StripTags(input, "p", "br");
    
        Assert.AreEqual(expected, actual);
    }
    

    它可能无法处理所有事情,但它可以满足我的需求。

    【讨论】:

    • 效果很好,可能人家懒得看你的回答了,因为是最长的
    【解决方案6】:

    也许这就是您要找的东西?

    foreach (HtmlNode node in html.DocumentNode.SelectNodes("//removeme"))
    {
        HtmlNodeCollection children = node.ChildNodes; //get <removeme>'s children
        HtmlNode parent = node.ParentNode; //get <removeme>'s parent
        node.Remove(); //remove <removeme>
        parent.AppendChildren(children); //append the children to the parent
    }
    

    编辑:L.B 的答案更清晰。和他一起去!

    【讨论】:

      【解决方案7】:

      这个怎么样?

      var removedNodes = document.SelectNodes("//removeme");
      if(removedNodes != null)
          foreach(var rn in removedNodes){
              HtmlTextNode innernodes =document.CreateTextNode(rn.InnerHtml);
              rn.ParnetNode.ReplaceChild(innernodes, rn);
          }
      

      【讨论】:

        【解决方案8】:

        通常正确的表达式是node.ParentNode.RemoveChildren(node, true)

        由于HtmlNode.RemoveChildren() (http://htmlagilitypack.codeplex.com/discussions/79587) 中的排序错误,我创建了一个类似的方法。对不起,它在VB中。如果有人想要翻译,我会写一个。

        'The HTML Agility Pack (1.4.9) includes the HtmlNode.RemoveChild() method but it has an ordering bug with preserving child nodes.  
        'The below implementation orders children correctly.
        Private Shared Sub RemoveNode(node As HtmlAgilityPack.HtmlNode, keepChildren As Boolean)
            Dim parent = node.ParentNode
            If keepChildren Then
                For i = node.ChildNodes.Count - 1 To 0 Step -1
                    parent.InsertAfter(node.ChildNodes(i), node)
                Next
            End If
            node.Remove()
        End Sub
        

        我已经使用以下测试标记测试了这段代码:

        <removeme>
            outertextbegin
            <p>innertext1</p>
            <p>innertext2</p>
            outertextend
        </removeme>
        

        输出是:

        outertextbegin
        <p>innertext1</p>
        <p>innertext2</p>
        outertextend
        

        【讨论】:

          【解决方案9】:

          这是 C# 中的版本 - 2014 年 12 月 3 日 17:57 的帖子的答案 - 伪编码器

          该网站不允许我评论和添加到原始帖子。也许它会帮助某人。

          private void removeNode(HtmlAgilityPack.HtmlNode node, bool keepChildren)
          {
              var parent = node.ParentNode;
              if (keepChildren)
              {
                  for ( int i = node.ChildNodes.Count - 1; i >= 0; i--)
                  {
                      parent.InsertAfter(node.ChildNodes[i], node);
                  }            
              }
              node.Remove(); 
          }
          

          【讨论】:

            【解决方案10】:

            您可以使用正则表达式还是需要使用 htmlagilitypack?

            string html = "<ul><removeMe><li><a href="#">Keep me</a></li></removeMe></ul>";
            
            html = Regex.Replace(html, "<removeMe.*?>", "", RegexOptions.Compiled);
            html = Regex.Replace(html, "</removeMe>", "", RegexOptions.Compiled);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-10-04
              • 1970-01-01
              • 1970-01-01
              • 2011-09-14
              • 2011-05-13
              • 1970-01-01
              • 2011-09-27
              相关资源
              最近更新 更多