替换 Nokogiri 节点中的部分文本,同时保留内容中的标记
Replacing part of the text in a Nokogiri node while preserving markup in contents
我试图通过使用 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"><<2>>Header</p>
<p class="paragraph">
<p class="text_style">Lorem ipsum blah blah blah. <<3>> Here is more content. <span class="style">Preserve this.</span> Blah blah extra text.</p>
</div>
</body>
</html>
全文都是数字,被<<
和>>
包围。我想获取数字的值并将其转换为这样的标记:<a id='[#]'/>
,但我想保留同一部分中其他元素的 HTML 标记,即 <span class="style">Preserve this.</span>
.
这是我尝试过的一切:
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
方法,所以任何标记都将被忽略并有效地清除。在这种情况下, <span class="style">Preserve this.</span>
部分被完全删除。 (仅供参考,我使用 .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
字符未正确转义:文件以 <a id="3"/>
而不是 <a id="3"/>
结束。
我尝试使用 CSS 方法,而不是像这样:
file.xpath("*").each { |node|
if node.inner_html.match(/<<([^_]*)>>/)
new_content = node.inner_html.gsub(/<<([^_]*)>>/,"<a id=\"\1\"/>")
node.inner_html = new_content
end
}
gsub
有效,标记被保留,被替换的标签被正确转义。但是删除了 <head>
和 <body>
标签,导致文件无效:
<?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("*")
) 这一事实有关,这也是多余的,因为除了子节点之外还扫描了父节点。
我在网上搜索过,但找不到任何解决方案。我只是希望能够在保持标记并正确编码的同时换出唯一的文本。有什么非常明显的东西是我在这里遗漏的吗?
看起来效果不错:
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"><<2>>Header</p>
<p class="paragraph">
<p class="text_style">Lorem ipsum. <<3>> 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="[]" />'))
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(.,'<<')]"
仅查找包含 '<<'
的文本节点。如果可能导致误报,您可能想要修改它以使其更具体。语法见“XPath: using regex in contains function”。
replace
正在表演;您试图修改 Nokogiri::XML::Text 节点以包含 <a.../>
,但它不能,必须对 <
和 >
进行编码。将节点更改为 Nokogiri::XML::Element,这是 Nokogiri 默认的 <a id="[2]">
,让它按照你的需要存储它。
我试图通过使用 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"><<2>>Header</p>
<p class="paragraph">
<p class="text_style">Lorem ipsum blah blah blah. <<3>> Here is more content. <span class="style">Preserve this.</span> Blah blah extra text.</p>
</div>
</body>
</html>
全文都是数字,被<<
和>>
包围。我想获取数字的值并将其转换为这样的标记:<a id='[#]'/>
,但我想保留同一部分中其他元素的 HTML 标记,即 <span class="style">Preserve this.</span>
.
这是我尝试过的一切:
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
方法,所以任何标记都将被忽略并有效地清除。在这种情况下, <span class="style">Preserve this.</span>
部分被完全删除。 (仅供参考,我使用 .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
字符未正确转义:文件以 <a id="3"/>
而不是 <a id="3"/>
结束。
我尝试使用 CSS 方法,而不是像这样:
file.xpath("*").each { |node|
if node.inner_html.match(/<<([^_]*)>>/)
new_content = node.inner_html.gsub(/<<([^_]*)>>/,"<a id=\"\1\"/>")
node.inner_html = new_content
end
}
gsub
有效,标记被保留,被替换的标签被正确转义。但是删除了 <head>
和 <body>
标签,导致文件无效:
<?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("*")
) 这一事实有关,这也是多余的,因为除了子节点之外还扫描了父节点。
我在网上搜索过,但找不到任何解决方案。我只是希望能够在保持标记并正确编码的同时换出唯一的文本。有什么非常明显的东西是我在这里遗漏的吗?
看起来效果不错:
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"><<2>>Header</p>
<p class="paragraph">
<p class="text_style">Lorem ipsum. <<3>> 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="[]" />'))
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(.,'<<')]"
仅查找包含 '<<'
的文本节点。如果可能导致误报,您可能想要修改它以使其更具体。语法见“XPath: using regex in contains function”。
replace
正在表演;您试图修改 Nokogiri::XML::Text 节点以包含 <a.../>
,但它不能,必须对 <
和 >
进行编码。将节点更改为 Nokogiri::XML::Element,这是 Nokogiri 默认的 <a id="[2]">
,让它按照你的需要存储它。