【问题标题】:Search text between different tags using XPath使用 XPath 在不同标签之间搜索文本
【发布时间】:2014-03-17 09:29:51
【问题描述】:

我正在使用 HtmlAgilityPack,我需要在 Html 文档中找到一个短语。例如我有一个文件:

<!DOCTYPE html>
<html>
<body>

<h1>aaa Heading ilo araferi</h1>

Thats <p>My <b>first</b> paragraph.</p>
<p>My second paragraph.</p>
<p>My third paragraph.</p>

</body>
</html>

我想将"Thats &lt;p&gt;My &lt;b&gt;first&lt;/b&gt; paragraph.&lt;/p&gt;" 包裹在span 中。为此,我需要仅使用文本(无 html 标签)查找所有出现的事件。例如

这是我的第一段。

换句话说,我希望短语 Thats My first paragraph. 匹配 Thats &lt;p&gt;My &lt;b&gt;first&lt;/b&gt; paragraph.&lt;/p&gt;

问题是,我不知道如何为这个特定任务执行 XPath 查询。任何帮助将不胜感激。谢谢

【问题讨论】:

  • For that, I need to find all occurences with text??不清楚...
  • 谢谢。我更新了问题。我不是以英语为母语的人,给您带来的不便,我深表歉意:)
  • 您不能在 HTML 中的 span 标签内包裹段落标签。请改用 div 标签。

标签: c# html xpath html-agility-pack


【解决方案1】:

编辑:已更新,因此 html 在 span 替换后仍然有效

using System.Collections.Generic;
using System.IO;
using System.Text;
using HtmlAgilityPack;
using System;

namespace Test {
  class Program {
    static void Main(string[] args) {
      var markup = @"<!DOCTYPE html>
    <html>
    <body>

    <h1>aaa Heading ilo araferi</h1>

    Thats <p>My <b>first</b> paragraph.</p>
    <p>My second paragraph.</p>
    <p>My third paragraph.</p>

    </body>
    </html>";
      var doc = new HtmlDocument();
      doc.LoadHtml(markup);
      var map = new List<HtmlNode>();

      var nodes = doc.DocumentNode.SelectNodes("//text()");
      var builder = new StringBuilder(markup.Length);
      for (var j = 0; j < nodes.Count; j++) {
        var node = nodes[j];
        builder.Append(node.InnerHtml);
        for (var i = 0; i < node.InnerHtml.Length; i++) {
          map.Add(node);
        }
      }

      var keyword = "Thats My first paragraph.";
      int index = builder.ToString().IndexOf(keyword);
      if (index >= 0) {
        var firstNode = map[index];
        var lastNode = map[index + keyword.Length - 1];
        var ancestor = Ancestor(firstNode, lastNode);
        if (ancestor != null) {
          while (firstNode != null && Level(firstNode) - Level(ancestor) > 1) {
            firstNode = firstNode.ParentNode;
          }
          while (lastNode != null && Level(lastNode) - Level(ancestor) > 1) {
            lastNode = lastNode.ParentNode;
          }
          if (firstNode != null && lastNode != null && ancestor == Ancestor(firstNode, lastNode)) {
            var span = doc.CreateElement("span");
            ancestor.ChildNodes.Insert(ancestor.ChildNodes.IndexOf(firstNode), span);
            int start = ancestor.ChildNodes.IndexOf(firstNode);
            int end = ancestor.ChildNodes.IndexOf(lastNode);
            for (var i = start; i <= end; i++) {
              var node = ancestor.ChildNodes[start];
              ancestor.ChildNodes.Remove(start);
              span.ChildNodes.Append(node);
            }
          }
        }
      }
      var writer = new StringWriter();
      doc.Save(writer);
      markup = writer.ToString();
    }

    public static HtmlNode Ancestor(HtmlNode a, HtmlNode b) {
      if (a == null) {
        throw new ArgumentNullException("a");
      }
      if (b == null) {
        throw new ArgumentNullException("b");
      }

      var parentsOfA = new List<HtmlNode>();
      while (a != null) {
        parentsOfA.Add(a);
        a = a.ParentNode;
      }

      while (b != null) {
        if (parentsOfA.Contains(b)) {
          return b;
        }
        b = b.ParentNode;
      }
      return null;
    }

    public static int Level(HtmlNode node) {
      int level = 0;
      while (node != null) {
        level++;
        node = node.ParentNode;
      }
      return level;
    }
  }
}

【讨论】:

  • 谢谢 Ondrej。我感谢您的帮助。不过有一个小问题,将内容包装在 span 中会破坏

    标签。 span 将在 p 标签之前打开,并将在 p 标签之前关闭。问题并不像看起来那么简单:(

  • @Davita - 你是对的,要让替换后的 html 有效,你需要找到共同的祖先并以 HtmlNode 方式而不是简单的文本方式替换跨度。我已经相应地更新了我的答案。
猜你喜欢
  • 2013-05-04
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多