Nokogiri儿童法
Nokogiri children method
我这里有以下XML:
<listing>
<seller_info>
<payment_types>Visa, Mastercard, , , , 0, Discover, American Express </payment_types>
<shipping_info>siteonly, Buyer Pays Shipping Costs </shipping_info>
<buyer_protection_info/>
<auction_info>
<bid_history>
<item_info>
</listing>
以下代码可以很好地显示第一个 //listing
节点的第一个子节点:
require 'nokogiri'
require 'open-uri'
html_data = open('http://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/data/auctions/321gone.xml')
nokogiri_object = Nokogiri::XML(html_data)
listing_elements = nokogiri_object.xpath("//listing")
puts listing_elements[0].children[1]
这也有效:
puts listing_elements[0].children[3]
我尝试使用以下代码访问第二个节点 <payment_types>
:
puts listing_elements[0].children[2]
但是显示了一个空行。翻看Firebug,明明是listing节点的2nd child。一般来说,只有奇数才适用于 children 方法。
这是 Nokogiri 中的错误吗?有什么想法吗?
这不是错误,它是在解析包含 "\n"
(或空节点)的字符串时创建的 space,但您可以使用 noblanks
选项来避免它们:
nokogiri_object = Nokogiri::XML(html_data) { |conf| conf.noblanks }
使用它,您的数组中将没有 空白。
问题是您没有正确解析文档。 children
returns 比你想象的要多,它的用途是把你逼到墙角。
这是我如何操作的简化示例:
require 'nokogiri'
doc = Nokogiri::XML(DATA.read)
auctions = doc.search('listing').map do |listing|
seller_info = listing.at('seller_info')
auction_info = listing.at('auction_info')
hash = [:seller_name, :seller_rating].each_with_object({}) do |s, h|
h[s] = seller_info.at(s.to_s).text.strip
end
[:current_bid, :time_left].each do |s|
hash[s] = auction_info.at(s.to_s).text.strip
end
hash
end
__END__
<?xml version='1.0' ?>
<!DOCTYPE root SYSTEM "http://www.cs.washington.edu/research/projects/xmltk/xmldata/data/auctions/321gone.dtd">
<root>
<listing>
<seller_info>
<seller_name>537_sb_3 </seller_name>
<seller_rating> 0</seller_rating>
</seller_info>
<auction_info>
<current_bid> 9.93</current_bid>
<time_left> 1 Day, 6 Hrs</time_left>
</auction_info>
</listing>
<listing>
<seller_info>
<seller_name> lapro8</seller_name>
<seller_rating> 0</seller_rating>
</seller_info>
<auction_info>
<current_bid> 0.00</current_bid>
<time_left> 4 Days, 21 Hrs</time_left>
</auction_info>
</listing>
</root>
在 运行 之后,auctions
将是:
auctions
# => [{:seller_name=>"537_sb_3",
# :seller_rating=>"0",
# :current_bid=>"9.93",
# :time_left=>"1 Day, 6 Hrs"},
# {:seller_name=>"lapro8",
# :seller_rating=>"0",
# :current_bid=>"0.00",
# :time_left=>"4 Days, 21 Hrs"}]
请注意,没有要处理的空文本节点,因为我告诉了 Nokogiri 从哪些节点中获取文本。您应该能够扩展代码以轻松获取所需的任何信息。
显示嵌套或缩进的典型格式化 XML 或 HTML 文档使用文本节点提供缩进:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<p>foo</p>
</body>
</html>
EOT
这是您的代码看到的内容:
doc.at('body').children.map(&:to_html)
# => ["\n" +
# " ", "<p>foo</p>", "\n" +
# " "]
文本节点让您感到困惑:
doc.at('body').children.first.class # => Nokogiri::XML::Text
doc.at('body').children.first.text # => "\n "
如果您的下钻不够深,您将拾取文本节点并必须清理结果:
doc.at('body')
.text # => "\n foo\n "
.strip # => "foo"
相反,明确找到您想要的节点并提取信息:
doc.at('body p').text # => "foo"
在上面的建议代码中,我使用了 strip
,因为传入的 XML 在某些文本周围有空格:
h[s] = seller_info.at(s.to_s).text.strip
这是原始 XML 创建代码在生成 XML 之前未清理行的结果。所以有时我们不得不清理他们的烂摊子,但是节点的正确访问可以减少很多。
问题是 children
包括文本节点,例如元素之间的空白。相反,如果您使用 element_children
,您只会得到子元素(即标签的内容,而不是周围的空白)。
我这里有以下XML:
<listing>
<seller_info>
<payment_types>Visa, Mastercard, , , , 0, Discover, American Express </payment_types>
<shipping_info>siteonly, Buyer Pays Shipping Costs </shipping_info>
<buyer_protection_info/>
<auction_info>
<bid_history>
<item_info>
</listing>
以下代码可以很好地显示第一个 //listing
节点的第一个子节点:
require 'nokogiri'
require 'open-uri'
html_data = open('http://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/data/auctions/321gone.xml')
nokogiri_object = Nokogiri::XML(html_data)
listing_elements = nokogiri_object.xpath("//listing")
puts listing_elements[0].children[1]
这也有效:
puts listing_elements[0].children[3]
我尝试使用以下代码访问第二个节点 <payment_types>
:
puts listing_elements[0].children[2]
但是显示了一个空行。翻看Firebug,明明是listing节点的2nd child。一般来说,只有奇数才适用于 children 方法。
这是 Nokogiri 中的错误吗?有什么想法吗?
这不是错误,它是在解析包含 "\n"
(或空节点)的字符串时创建的 space,但您可以使用 noblanks
选项来避免它们:
nokogiri_object = Nokogiri::XML(html_data) { |conf| conf.noblanks }
使用它,您的数组中将没有 空白。
问题是您没有正确解析文档。 children
returns 比你想象的要多,它的用途是把你逼到墙角。
这是我如何操作的简化示例:
require 'nokogiri'
doc = Nokogiri::XML(DATA.read)
auctions = doc.search('listing').map do |listing|
seller_info = listing.at('seller_info')
auction_info = listing.at('auction_info')
hash = [:seller_name, :seller_rating].each_with_object({}) do |s, h|
h[s] = seller_info.at(s.to_s).text.strip
end
[:current_bid, :time_left].each do |s|
hash[s] = auction_info.at(s.to_s).text.strip
end
hash
end
__END__
<?xml version='1.0' ?>
<!DOCTYPE root SYSTEM "http://www.cs.washington.edu/research/projects/xmltk/xmldata/data/auctions/321gone.dtd">
<root>
<listing>
<seller_info>
<seller_name>537_sb_3 </seller_name>
<seller_rating> 0</seller_rating>
</seller_info>
<auction_info>
<current_bid> 9.93</current_bid>
<time_left> 1 Day, 6 Hrs</time_left>
</auction_info>
</listing>
<listing>
<seller_info>
<seller_name> lapro8</seller_name>
<seller_rating> 0</seller_rating>
</seller_info>
<auction_info>
<current_bid> 0.00</current_bid>
<time_left> 4 Days, 21 Hrs</time_left>
</auction_info>
</listing>
</root>
在 运行 之后,auctions
将是:
auctions
# => [{:seller_name=>"537_sb_3",
# :seller_rating=>"0",
# :current_bid=>"9.93",
# :time_left=>"1 Day, 6 Hrs"},
# {:seller_name=>"lapro8",
# :seller_rating=>"0",
# :current_bid=>"0.00",
# :time_left=>"4 Days, 21 Hrs"}]
请注意,没有要处理的空文本节点,因为我告诉了 Nokogiri 从哪些节点中获取文本。您应该能够扩展代码以轻松获取所需的任何信息。
显示嵌套或缩进的典型格式化 XML 或 HTML 文档使用文本节点提供缩进:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<p>foo</p>
</body>
</html>
EOT
这是您的代码看到的内容:
doc.at('body').children.map(&:to_html)
# => ["\n" +
# " ", "<p>foo</p>", "\n" +
# " "]
文本节点让您感到困惑:
doc.at('body').children.first.class # => Nokogiri::XML::Text
doc.at('body').children.first.text # => "\n "
如果您的下钻不够深,您将拾取文本节点并必须清理结果:
doc.at('body')
.text # => "\n foo\n "
.strip # => "foo"
相反,明确找到您想要的节点并提取信息:
doc.at('body p').text # => "foo"
在上面的建议代码中,我使用了 strip
,因为传入的 XML 在某些文本周围有空格:
h[s] = seller_info.at(s.to_s).text.strip
这是原始 XML 创建代码在生成 XML 之前未清理行的结果。所以有时我们不得不清理他们的烂摊子,但是节点的正确访问可以减少很多。
问题是 children
包括文本节点,例如元素之间的空白。相反,如果您使用 element_children
,您只会得到子元素(即标签的内容,而不是周围的空白)。