2

我有一些 HTML:

<p>Lorem ipsum example laoreet. <a href="#">example</a>Cum porttitor</p>
<p>Phasellus <a href="#">gravida tempor example</a> magna</p>

我需要在 HTML 中出现的文本“示例”的任何实例周围包裹一个跨度,除非它位于锚标记内。这样上面就变成了:

<p>Lorem ipsum <span class="something">example</span> laoreet. <a href="#">example</a>Cum porttitor</p>
<p>Phasellus <a href="#">gravida tempor example</a> posuere. Fusce vitae urna eu <span class="something">example</span> magna</p>

我可以使用以下方法选择不在锚标记内的段落内容:

doc.xpath('//p//text()') - doc.xpath('//p//a/text()')

我可以使用以下方法将标签包装在另一个标签的文本内容周围:

doc.search('div.some-class text()').wrap('<span class="something"></span>')

但是如何在该内容中围绕文本包装标签?

4

4 回答 4

1

text()Xpath 选择器也可以用来匹配这样的文本:Using
XPath, How do I select a node based on its text content and value of an attribute?

doc.xpath("//p//text()='example'")

但我认为这行不通:

doc.search("div.some-class text()='example'").wrap('<span class="something"></span>')
于 2013-08-14T11:54:41.400 回答
1

您可能必须在 Ruby 中操作有问题的文本节点,然后replace在文档中使用 Nokogiri 将为您解析的新文本。

doc.xpath('//p/descendant-or-self::node()[name() != "a"]/text()[contains(., "example")]').each do |n|
  n.replace(n.content.gsub(/(example)/, '<span class="something">\1</span>'))
end

在此示例中,我使用了比您使用的稍微复杂的 XPath 查询。p它选择任何元素的所有文本节点后代,除非它们是a元素的后代,我认为这是您想要的。(我不知道这是否对你更好,试试看。)

回答您问题的部分是块的内容。在这里,我获取每个文本节点的字符串内容,并使用其中的新元素gsub创建一个新的标记字符串。然后我用这个片段代替文档中的原始文本节点。Nokogiri 将解析此字符串并添加创建的节点来代替原始文本节点。这在很多方面类似于Tin Man 的答案,但更有针对性,因为它只涉及使用和重新解析有问题的文本节点。spanreplacegsub

于 2013-08-14T16:59:44.500 回答
0

这是我最后的做法:

doc = Nokogiri::HTML(html)
# Select paragraph content that isn't inside an anchor tag
elements = doc.xpath('//p//text()') - doc.xpath('//p//a/text()')
# interate over the elements, wrapping 'phrase' with anchor tag
elements.each do |element|
    element.content = element.content.gsub(phrase, "<a href='#' class='glossary-term-link' data-content='#{definition.html_safe}'>#{phrase}</a>")
end
# Fix Nokogiri's lust for escaping angle brackets no matter what
doc.xpath('//body')[0].inner_html.gsub("&lt;", "<").gsub("&gt;", ">")
于 2013-08-14T18:11:32.037 回答
0

这是我的做法:

require 'nokogiri'

doc = Nokogiri::HTML(<<EOT)
<p>Lorem ipsum example sit amet. <a href="#">example</a>Sed porttitor</p>
<p>Phasellus <a href="#">tempor example</a> posuere. Example </p>
EOT

a_tags = doc.search('a')

new_doc = Nokogiri::HTML(
  doc.to_html.gsub(
    /\b (example) \b/ix,
    '<span class="foo">\1</span>'
  )
)
new_doc.search('a').each do |a_tag|
  a_tag.replace(a_tags.shift)
end

puts new_doc.to_html
# >> </body></html>
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html><body>
# >> <p>Lorem ipsum <span class="foo">example</span> sit amet. <a href="#">example</a>Sed porttitor</p>
# >> <p>Phasellus <a href="#">tempor example</a> posuere. <span class="foo">Example</span> </p>
# >> </body></html>

基本上它是这样做的:

  • a_tags = doc.search('a')抓取所有现有<a>标签以记住它们。
  • doc使用 Nokogiri 将 DOM 转换回 HTML 以保持一致性to_html,然后使用 进行全局搜索和替换以将所有“示例”实例包装在 a 中<span>,然后将其重新解析为新的 DOM。请注意,我/\b (example) \b/ix用于搜索和\1替换。为什么我使用捕获和标志供您研究,但您应该注意到它让我找到并处理“示例”或“示例”。
  • 遍历文档<a>再次查找标签,并将每个标签替换为其原始版本。gsub这将清除上一步中被破坏的任何内容。

这比我喜欢的要暴力一点,但它也是直截了当的。如果在标签中找到单词“example”,这将中断。

也许聪明的 XPath 人之一会加入一些更优雅的东西。

于 2013-08-14T14:57:21.973 回答