如何使用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)

我打开文件的方式不对吗?

有几点不对劲。第一个是,你要求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会返回一个NodeSet,它不理解content=search以及cssxpath返回NodeSet。您可以迭代返回的NodeSet并处理单个节点,但按原样,试图将相同的内容设置到NodeSet中的一堆节点是不合乎逻辑的,因此Nokogiri没有实现它。

相反:

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

atat_cssat_xpath等效于使用search('some selector').first,这就是为什么它们只返回一个Node。

现在来看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>

Nokogiri将向xpath查询返回一个NodeSet(还有searchcss)。这是Node的的可枚举对象

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

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

或者,如果需要,您可以使用.each在NodeSet中循环

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

相关内容

  • 没有找到相关文章