用Nokogiri包装不在锚标记内的文本

时间:2013-08-14 11:51:46

标签: html ruby parsing nokogiri

我有一些HTML:

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

我需要在HTML中出现的文本“example”的任何实例周围换行,除非它在锚标记内。这样就可以了:

<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 个答案:

答案 0 :(得分: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>')

答案 1 :(得分: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创建一个新的标记字符串,其中包含新的span元素。然后我使用replace将此片段放入原始文本节点在文档中的位置。 Nokogiri将解析此字符串并添加创建的节点来代替原始文本节点。这在很多方面与the Tin Man’s answer类似,但更具针对性,因为它只涉及使用gsub并重新解析相关的文本节点。

答案 2 :(得分: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>代码以便记住它们。
  • 我使用Nokogiri将doc DOM转换回HTML以使用to_html保持一致性,然后执行全局搜索并替换以包裹<span>中的所有“示例”实例,然后重新分析它成为一个新的DOM。请注意,我正在使用/\b (example) \b/ix进行搜索,并使用\1进行替换。为什么我使用捕获并且标记是供您研究的,但您应该注意到它让我找到并处理“示例”或“示例”。
  • 循环浏览文档,再次查找<a>标记,并用原始版本替换每个标记。这将清除上一步中gsub所造成的任何损坏。

这比我喜欢的更强大,但它也是直截了当的。如果在标签内找到“example”一词,这将会中断。

也许其中一位聪明的XPath人会用更优雅的东西加入。

答案 3 :(得分: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;", ">")