【问题标题】:Replacing part of the text in a Nokogiri node while preserving markup in contents替换 Nokogiri 节点中的部分文本,同时保留内容中的标记
【发布时间】:2020-05-15 11:16:41
【问题描述】:

我正在尝试通过使用 Nokogiri 扫描节点的内容然后执行 gsub 来替换一堆文件中唯一字符串的实例。我将字符串的一部分保留在适当的位置,并将其转换为锚标记。但是,大多数节点在内容中都有各种形式的标记,而不仅仅是简单的字符串。例如,假设我有一个这样的文件:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
    <head>
        <title>Title</title>
        <link href="style.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <div>
            <p class="header">&lt;&lt;2&gt;&gt;Header</p>
            <p class="paragraph">
            <p class="text_style">Lorem ipsum blah blah blah. &lt;&lt;3&gt;&gt; Here is more content. <span class="style">Preserve this.</span> Blah blah extra text.</p>
        </div>
    </body>
</html>

整个文档都有数字,由&amp;lt;&amp;lt;&amp;gt;&amp;gt; 包围。我想获取数字的值并将其转换为这样的标签:&lt;a id='[#]'/&gt;,但我想保留同一部分中其他元素的 HTML 标记,即&lt;span class="style"&gt;Preserve this.&lt;/span&gt;

这是我尝试过的所有内容:

file = File.open("file.xhtml") {|f| Nokogiri::XML(f)}

file.xpath("//text()").each { |node|
    if node.text.match(/<<([^_]*)>>/)
        new_content = node.text.gsub(/<<([^_]*)>>/,"<a id=\"\\1\"/>")
        node.parent.inner_html = new_content
    end
}

gsub 可以正常工作,但是因为它使用了.text 方法,所以任何标记都会被忽略并有效地清除。在这种情况下,&lt;span class="style"&gt;Preserve this.&lt;/span&gt; 部分被完全删除。 (仅供参考,我使用.parent 方法,因为如果我只使用node.inner_html = new_content,我会收到此错误:add_child_node': cannot reparent Nokogiri::XML::Element there (ArgumentError)。)

如果我这样做:

    new_content = node.text.gsub(/<<([^_]*)>>/,"<a id=\"\\1\"/>")
    node.content = new_content

字符未正确转义:文件以 &amp;lt;a id="3"/&amp;gt; 而不是 &lt;a id="3"/&gt; 结尾。

我尝试使用 CSS 方法,如下所示:

file.xpath("*").each { |node|
    if node.inner_html.match(/&lt;&lt;([^_]*)&gt;&gt;/)
        new_content = node.inner_html.gsub(/&lt;&lt;([^_]*)&gt;&gt;/,"<a id=\"\\1\"/>")
        node.inner_html = new_content
    end
}

gsub 有效,标记被保留,被替换的标签被正确转义。但是&lt;head&gt;&lt;body&gt;标签被删除,导致文件无效:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Title</title>
        <link href="style.css" rel="stylesheet" type="text/css"/>
        <div>
            <p class="header"><a id="2"/>Header</p>
            <p class="paragraph">
            </p><p class="text_style">Lorem ipsum blah blah blah. <a id="3"/> Here is more content. <span class="style">Preserve this.</span> Blah blah extra text. </p>    
    </div>
</html>

我怀疑这与我正在遍历所有节点 (file.css("*")) 的事实有关,这也是多余的,因为除了其子节点之外还扫描了父节点。

我在网上搜索过,但找不到任何解决方案。我只是希望能够在保持标记并对其进行正确编码的同时换出独特的文本。我在这里遗漏了什么非常明显的东西吗?

【问题讨论】:

  • 这是一种查找要修改的节点的可怕方法。而是使用选择器找到您要更改的确切节点,然后更改它们的text。如果您负责标记,则将一些有用的类信息放在节点中,以便于查明它们。基本上你正在创建一个模板引擎。
  • @theTinMan 是的,这不是我想要找到节点的方式,这就是我首先问这个问题的原因。问题是我正在搜索的唯一文本可以出现在任何地方、任何节点、任何选择器中——我不知道“确切节点”是什么,这就是重点。我提供了一个简单的 HTML doc 供参考,但实际内容是通过 InDesign 导出的,比较复杂、冗长且不可预测。
  • 总是定义类吗?而且,为什么它被定义为 XML 而它却是 HTML?搜索文本的问题是,textcontent 或任何寻找文本的选择器都会在该节点的子节点中找到它,即使它嵌套得很深。您可能想在 Nokogiri 支持小组上提问。 groups.google.com/forum/#!forum/nokogiri-talk

标签: ruby nokogiri


【解决方案1】:

看起来这很有效:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
    <head>
        <title>Title</title>
        <link href="style.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <div>
            <p class="header">&lt;&lt;2&gt;&gt;Header</p>
            <p class="paragraph">
            <p class="text_style">Lorem ipsum. &lt;&lt;3&gt;&gt; more content. <span class="style">Preserve this.</span> extra text.</p>
        </div>
    </body>
</html>
EOT

doc.search("//text()[contains(.,'<<')]").each do |node|
  node.replace(node.content.gsub(/<<(\d+)>>/, '<a id="[\1]" />'))
end

结果:

puts doc.to_html

# >> <html>
# >>     <head>
# >> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
# >>         <title>Title</title>
# >>         <link href="style.css" rel="stylesheet" type="text/css">
# >>     </head>
# >>     <body>
# >>         <div>
# >>             <p class="header"><a id="[2]"></a>Header</p>
# >>             <p class="paragraph">
# >>             <p class="text_style">Lorem ipsum. <a id="[3]"></a> more content. <span class="style">Preserve this.</span> extra text.</p>
# >>         </p>
# >>     </div>
# >> </body>
# >> </html>

Nokogiri 正在添加

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

行,可能是因为标记被定义为 XML。

选择器"//text()[contains(.,'&lt;&lt;')]" 只查找包含'&lt;&lt;' 的文本节点。如果可能导致误报,您可能需要对其进行修改以使其更加具体。语法见“XPath: using regex in contains function”。

replace 正在执行此操作;您试图修改 Nokogiri::XML::Text 节点以包含 &lt;a.../&gt;,但它不能,必须对 &lt;&gt; 进行编码。将节点更改为 Nokogiri::XML::Element,这是 Nokogiri 默认 &lt;a id="[2]"&gt; 的内容,让它可以根据需要存储它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 2020-11-24
    • 2011-04-17
    相关资源
    最近更新 更多