1

我有很多大型 (32 Mb) XML 文件,其中包含来自不同商店的产品信息。我正在使用托管在 Heroku 上的 Rails。

我想解析这些 XML 提要并将这些产品写入我的数据库。我有一个半工作的解决方案,但它非常慢并且太占用内存。

到目前为止,我或多或少都在使用这个:

open_uri_fetched = open(xml_from_url)
xml_list = Nokogiri::HTML(open_uri_fetched)
xml_list.xpath("//product").each do |product|
// parsed nodes
// Model.create()
end

这在一定程度上起到了作用。但是,这导致 Heroku 出现内存问题,导致脚本崩溃。它也很慢(我为 200 多个提要执行此操作)。

Heroku 告诉我通过使用 Nokogiri::XML::Reader 来解决问题,这就是我现在想要做的。

我也研究过使用:

ActiveRecord::Base.transaction do
Model.create()
end

加快 Model.create() 过程。

1. 我的第一个问题:这是解决我的问题的正确方法(或至少是体面的方法)吗?

现在,这就是我尝试做的:

  reader = Nokogiri::XML::Reader(File.open('this_feed.xml'))
  reader.each do |node|
    if node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
      if node.name.downcase == xname
        puts "Name: " + node.inner_xml
        use_name = node.inner_xml
      end
    end
  end

问题 2:但是我在哪里放置模型创建代码?

ActiveRecord::Base.transaction do
  Model.create(:name => use_name)
end

如果我把它放在循环中,它会尝试为每个节点创建,这是错误的。我希望在 xml 列表中的每个产品之后调用它,对吗?

如果我创建一个在读取 XML 期间正在构建的哈希(然后用于创建模型创建),那会不会像首先打开 XML 文件那样占用大量内存?

简而言之,XML 文件看起来像这样:

<?xml version="1.0" encoding="UTF-8" ?>
<products>
    <product>
        <name>This cool product</name>
        <categories>
            <category>Food</category>
            <category>Drinks</category>
        </categories>
        <SKU />
        <EAN />
        <description>A long description...</description>
        <model />
        <brand />
        <gender />
        <price>126.00</price>
        <regularPrice>126.00</regularPrice>
        <shippingPrice />
        <currency>SEK</currency>
        <productUrl>http://www.domain.com/1.html</productUrl>
        <graphicUrl>http://www.domain.com/1.jpg</graphicUrl>
        <inStock />
        <inStockQty />
        <deliveryTime />
    </product>
</products>
4

1 回答 1

3

Reader 只需扫描一次文档。你必须自己跟踪状态:你看到了哪些元素,你是否在你关心的元素中,等等。

这个要点是一个鲜为人知的美,它极大地改进了 Reader 的语法。它以一种非常易于阅读的方式为您跟踪状态。

以下是如何使用它的示例,取自评论:

Xml::Parser.new(Nokogiri::XML::Reader(open(file))) do
  inside_element 'User' do
    for_element 'Name' do puts "Username: #{inner_xml}" end
    for_element 'Email' do puts "Email: #{inner_xml}" end

    for_element 'Address' do
      puts 'Start of address:'
      inside_element do
        for_element 'Street' do puts "Street: #{inner_xml}" end
        for_element 'Zipcode' do puts "Zipcode: #{inner_xml}" end
        for_element 'City' do puts "City: #{inner_xml}" end
      end
      puts 'End of address'
    end
  end
end

真的应该有人用这个小小的,嗯,宝石制作宝石。

在你的情况下,你可以有一个inside_element 'product'块,提取你需要的元素,并在你的产品块的末尾创建你的模型实例。

于 2012-09-19T13:09:31.567 回答