2013-07-04 56 views
1

我有一个HTML文档作为字符串。我分析它使用引入nokogiri:如何搜索和替换节点Nokogiri?

doc_str = <<-mydoc 
    <p>Lorem ipsum dolor sit foo.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p> 
mydoc 

doc = Nokogiri::HTML doc_str 

我想"foo"/"Foo"在所有可见的文本"Bar"/"bar"更换:

desired = <<-mydoc 
    <p>Lorem ipsum dolor sit bar.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Bar do <a href="/c-foo.aspx" class = "foo" title="Bar bar.">foofoo bar</a>.</p> 
mydoc 

我该怎么办呢?

我试图阅读Nokogiri tutorial,其中描述了Nokogiri::HTML::Document#at_css。使用Ruby 2.0和最新的Nokogiri,doc.at_css 'h1'返回nil,所以h1.content = "something"甚至不可能。

即使工作,它也只是解决我的查找和替换问题的第一步。

回答

2

doc.at_css 'h1'返回nil,因为HTML中没有h1元素。 doc.at_css 'h2'正确返回h2元素的Nokogiri::XML::Element对象。

CSS选择器无法选择文本节点,对于这类事情来说这是一个糟糕的工具。 XPath将完成CSS所做的所有工作,还有更多。文档根目录下的文本节点只是//text()

编辑我刚刚注意到,你似乎希望属性的内容以相同的方式改变。 @*与任何属性匹配,所以XPath表达式变为//@* | //text()。虽然我不清楚这一点,因为href="/c-foo.aspx"class="foo"保持不变,但title="Foo bar."变成title="bar bar."。我相信你可以自己解决这个问题。

您需要使用XPath查找所有文本节点,然后使用content来获取每个节点的文本值。根据需要修改它并使用content=来替换它。

该程序演示。方法to_html将数据封装在使其成为有效HTML的标签中。

require 'nokogiri' 

doc_str = <<-HTML 
    <p>Lorem ipsum dolor sit foo.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p> 
HTML 

doc = Nokogiri::HTML(doc_str) 

doc.xpath('//@*', '//text()').each do |node| 
    node.content = node.content.gsub(/\bfoo\b/, 'bar').gsub(/\bFoo\b/, 'Bar') 
end 

puts doc.to_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 dolor sit bar.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Bar do <a href="/c-bar.aspx" class="bar" title="Bar bar.">foofoo bar</a>.</p> 
</body></html> 
+0

+1教我这么多,但请注意,您的例子并不完全正确,因为它也更换'类= “foo” 的''到CLASS = “吧”,它不应该,因为它不是一个可见的文本。相反,@ pguardiario的'#遍历'示例不会遇到'class =“bar”'问题,他错过了''b''绕着'foo'和'Foo'环绕。你能告诉我更多关于'#xpath'方法的论点吗?我的意思是''// @ * | //文本()''。这是什么意思? –

+0

@BorisStitnicky:所以你的问题中的属性之间的差异是故意的?它一点都不清楚。你将不得不枚举你想要处理的属性名称,以使其正确。 XPath术语'text()'和'@ *'我已经解释过了。 “//”意味着“后代”,当它在表达式的开始时,它意味着“根的后代”,即文档中的任何地方。管道'|'是“联合”,因此完整的表达式可以在任何地方选择所有属性节点和所有文本节点。 [在这里查看XPath 1.0的规范。](http://www.w3.org/TR/xpath/) – Borodin

+0

将'@ *'更改为'@ title',我认为你已经完成了它。 –