为什么这个JSON文件被填充了最后一个哈希数据的1747倍?

时间:2014-06-18 23:23:32

标签: ruby json hash web-scraping mechanize

我使用以下代码生成包含特定网站所有类别信息的JSON文件。

require 'mechanize'

@hashes = []

@categories_hash = {}
@categories_hash['category'] ||= {}
@categories_hash['category']['id'] ||= {}
@categories_hash['category']['name'] ||= {}
@categories_hash['category']['group'] ||= {}

# Initialize Mechanize object
a = Mechanize.new

# Begin scraping
a.get('http://www.marktplaats.nl/') do |page|
  groups = page.search('//*[(@id = "navigation-categories")]//a')

  groups.each_with_index do |group, index_1|
    a.get(group[:href]) do |page_2|
      categories = page_2.search('//*[(@id = "category-browser")]//a')

      categories.each_with_index do |category, index_2|
        @categories_hash['category']['id'] = "#{index_1}_#{index_2}"
        @categories_hash['category']['name'] = category.text
        @categories_hash['category']['group'] = group.text

        @hashes << @categories_hash['category']

        # Uncomment if you want to see what's being written
        puts @categories_hash['category'].to_json
      end
    end
  end
end

File.open("json/magic/#{Time.now.strftime '%Y%m%d%H%M%S'}_magic_categories.json", 'w') do |f|
  puts '# Writing category data to JSON file'
  f.write(@hashes.to_json)
  puts "|-----------> Done. #{@hashes.length} written."
end

puts '# Finished.'

但是这段代码返回的JSON文件只填充了最后一个类别数据。对于完整的JSON文件,请查看here。这是一个示例:

[
   {
      "id":"36_17",
      "name":"Overige Diversen",
      "group":"Diversen"
   },
   {
      "id":"36_17",
      "name":"Overige Diversen",
      "group":"Diversen"
   },
   {
      "id":"36_17",
      "name":"Overige Diversen",
      "group":"Diversen"
   }, {...}
]

问题是,导致这种情况的原因是什么?如何解决?

1 个答案:

答案 0 :(得分:1)

相同的对象,@categories_hash['category']的结果,每个循环都在更新。

因此,数组用相同的对象填充1747次,并且该对象反映了在稍后查看时在最后一个循环上完成的突变。


虽然修复可能是使用@categories_hash[category_name]或类似的(即每个循环获取/确保不同的对象),但以下内容可避免所描述的问题以及未使用/误用的散列'类别'键。

categories.each_with_index do |category, index_2|
    # creates a new Hash object
    item = {
        id: "#{index_1}_#{index_2}",
        name: category.text,
        group: group.text
    }
    # adds the new (per yield) object
    @hashes << item
end

或者,更“实用”的方法可能是使用map,但它以相同的方式解决问题 - 通过创建 new [Hash]对象。 (这可以扩展到也包括外环,但它只是为了品味。)

h = categories.each_with_index.map do |category, index_2|
    {
        id: "#{index_1}_#{index_2}",
        name: category.text,
        group: group.text
    }
end
@hashes.concat(h)