如何使用Nokogiri修改节点内容?

时间:2014-09-30 20:40:53

标签: ruby 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)

我是否错误地打开了文件?

2 个答案:

答案 0 :(得分:3)

有几件事是错的。首先,你要求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>]

因此,除非该模板是有效的HTML,否则您不能指望Nokogiri处理模板。

在删除无效标记并简化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=的NodeSet。 search以及cssxpath会返回一个NodeSet。你可以遍历返回的NodeSet并处理单个节点,但是,按原样,尝试将相同的内容设置为NodeSet中的一堆节点是不合逻辑的,所以Nokogiri没有&# 39;实现它。

相反:

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

atat_cssat_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>

答案 1 :(得分:2)

Nokogiri会将NodeSet返回xpath个查询(同时searchcss)。这是Node s

的可枚举对象

如果您知道您的元素是唯一的元素:

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

或者,如果需要,您可以使用.each遍历NodeSet

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