2013-07-04 57 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',我認爲你已經完成了它。 –