Nokogiri儿童方法

时间:2017-05-24 02:08:19

标签: ruby xml-parsing nokogiri

我在这里有以下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,它显然是列表节点的第二个子节点。通常,只有奇数可以使用children方法。

这是Nokogiri的错误吗?有什么想法吗?

3 个答案:

答案 0 :(得分:6)

它不是一个错误,它是在解析包含"\n"(或空节点)的字符串时创建的空间,但您可以使用noblanks选项来避免它们:

nokogiri_object = Nokogiri::XML(html_data) { |conf| conf.noblanks }

使用它,你的阵列中没有空白

答案 1 :(得分:3)

问题是您没有正确解析文档。 children的回报超出了你的想象,它的使用正在把你画成一个角落。

以下是我如何做的简化示例:

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> $839.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> $210.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=>"$839.93",
#      :time_left=>"1 Day, 6 Hrs"},
#     {:seller_name=>"lapro8",
#      :seller_rating=>"0",
#      :current_bid=>"$210.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" +
#    "  "]

Text节点令您感到困惑:

doc.at('body').children.first.class # => Nokogiri::XML::Text
doc.at('body').children.first.text # => "\n    "

如果你没有向下钻取得足够远,你将选择Text节点并且必须清理结果:

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之前不清除行的结果。所以有时我们必须清理他们的混乱,但正确访问节点可以减少很多。

答案 2 :(得分:-1)

问题是children包括文本节点,例如元素之间的空白。相反,如果使用element_children,则只获得子元素(即标记的内容,而不是周围的空格)。