2014-09-30 3 views
2

У меня есть сценарий, как это:Как изменить содержимое узла с помощью Nokogiri?

require 'rubygems' 
require 'nokogiri' 
require 'json' 

data = File.read("data.json") 
obj = JSON.parse(data) 
puts obj.values 

@page = Nokogiri::HTML(open("template.panoramatemplate")) 

recipename = @page.xpath("//body/h1") 
recipename.content = "hello" 
puts teachername 

У меня есть файл HTML, который довольно простой:

<html> 
    <head> 
    <title><* page.title *></title> 
    </head> 
    <body> 
    <h1><* recipe.name *></h1> 
    <* food name *> 
     <* food.name *> 
     <* more values *> 
     <p><* value *></p> 
     <* ENDEACH *> 
    <* ENDEACH *> 
    </body> 
</html> 

Я смотрел на этот раздел: http://nokogiri.org/tutorials/modifying_an_html_xml_document.html

Первый пример изменения текста содержание. Я попытался следовать этому примеру, но получаю:

undefined method content= for [#<Nokogiri::XML::Element:0x80d1fb98 name="h1">]:Nokogiri::XML::NodeSet (NoMethodError) 

Я открываю документ неправильно?

ответ

1

Nokogiri возвратит NodeSet к xpath запрос на (также search и css). Это перечислимый объект Node s

Если вы знаете, что ваш элемент является единственным:

recipename = @page.xpath("//body/h1").first 

Или вы можете цикл через NodeSet с .each при необходимости

recipename = @page.xpath("//body/h1") 
recipename.each do |node| 
    puts node.content 
end 
+0

Nokogiri * возможно * вернуть NodeSet на запрос, в зависимости от используемого метода. 'search',' css' и 'xpath' возвращают NodeSet. 'at',' at_css' и 'at_xpath' возвращают узел. –

2

Есть несколько все неправильно. Во-первых, вы просите Nokogiri разобрать что-то, что выглядит вроде как HTML, но нет. Nokogiri достаточно умен, чтобы знать разницу:

require 'nokogiri' 

doc = Nokogiri::HTML(<<EOT) 
<html> 
    <head> 
    <title><* page.title *></title> 
    </head> 
    <body> 
    <h1><* recipe.name *></h1> 
    <* food name *> 
     <* food.name *> 
     <* more values *> 
     <p><* value *></p> 
     <* ENDEACH *> 
    <* ENDEACH *> 
    </body> 
</html> 
EOT 

puts doc.to_html 
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 
# >> <html> 
# >> <head> 
# >> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> 
# >>  <title></title> 
# >> </head> 
# >> <body> 
# >>  <h1></h1> 
# >>  
# >>  
# >>  
# >>   <p></p> 
# >>  
# >>  
# >> </body> 
# >> </html> 

Обратите внимание на куски на выходе? Вот что Nokogiri должен сказать о HTML:

doc.errors 
# => [#<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>, 
#  #<Nokogiri::XML::SyntaxError: htmlParseStartTag: invalid element name>] 

Таким образом, вы не можете ожидать, Nokogiri для обработки шаблона, если этот шаблон не является действительной HTML.

После зачистки недопустимых тегов и упрощения HTML, вот следующий вопрос:

require 'nokogiri' 

doc = Nokogiri::HTML(<<EOT) 
<html> 
    <body> 
    <h1>foo</h1> 
    </body> 
</html> 
EOT 

h1 = doc.search('h1') 
h1.class # => Nokogiri::XML::NodeSet 
h1.respond_to?(:content=) # => false 

Обратите внимание, что при использовании search возвращает набор узлов, который не понимает content=. search, а также css и xpath возвращает узел NodeSet. Вы могли бы перебирать возвращаемый NodeSet и работать с отдельными узлами, но, как и пытаться установить один и тот же контент в связку узлов в NodeSet, это не логично, поэтому Nokogiri не реализует его.

Вместо:

h1 = doc.at('h1') 
h1.class # => Nokogiri::XML::Element 
h1.respond_to?(:content=) # => true 
h1.content = 'hello' 

at, at_css и at_xpath эквивалентны использованию search('some selector').first, поэтому они возвращаются только узел.

Глядя на DOM сейчас:

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> 
# >>  <h1>hello</h1> 
# >> </body> 
# >> </html> 
+0

Ваш ответ, вероятно, лучший подход. – Jngai1297

+1

Это может, но вам нужно * понять, почему и почему вы, возможно, не захотите следовать этому конкретному пути. Важное значение имеет понимание того, что вы делаете, и как выборочно выбирать один способ сделать что-то против другого, особенно когда вы имеете дело с очень сложными документами, и скорость важна. –

+0

Я понимаю, но, к сожалению, этот шаблон имеет очень мало значения. – Jngai1297

Смежные вопросы