我有一个这样的脚本:
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
以及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
,这就是为什么它们只返回一个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
(还有search
和css
)。这是Node
的的可枚举对象
如果你知道你的元素是唯一的:
recipename = @page.xpath("//body/h1").first
或者,如果需要,您可以使用.each
在NodeSet中循环
recipename = @page.xpath("//body/h1")
recipename.each do |node|
puts node.content
end