"undefined method []" 解析 reddit 时 api

"undefined method []" when parsing reddit api

我正在尝试为多个 subreddits 请求 json 页面,并从大学项目的每个页面中获取标题和 link。这是有问题的代码:

require 'rufus-scheduler'
require 'json'
require 'httparty'
ENV['TZ'] = 'Europe/Dublin'

scheduler = Rufus::Scheduler::singleton


scheduler.every '12h00m', :first_at => Time.now + 10 do

 array_of_subreddits = ["pics", "memes", "funny", "aww", "memes", 
"birdswitharms"]
array_of_subreddits.each do |category|
sleep 10 #wait 10 seconds between each request

@response = JSON.parse(HTTParty.get("http://reddit.com/r/#{category}/.json?limit=25").body)

  @response['data']['children'].each do |data|
      @link = data['data']['url']
      @title = data['data']['title']
      @category = category
      Pic.create([{:title => "#{@title}", :link => "#{@link}", :category => "#{@category}"}])
    end
  end
end

这有时非常有效,它会 运行 遍历每一个并按预期结束。通常情况下,它会在一两次通过后给我这条消息:

NoMethodError (undefined method `[]' for nil:NilClass):
  app/controllers/home_controller.rb:17:in `block in index'
  app/controllers/home_controller.rb:9:in `each'
  app/controllers/home_controller.rb:9:in `index'


  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_source.erb (4.8ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.2ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.2ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (66.2ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_markup.html.erb (0.4ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.3ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.3ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.5ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/console.js.erb within layouts/javascript (51.6ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/main.js.erb within layouts/javascript (0.3ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.5ms)
  Rendered /Users/conorbreen/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/web-console-2.3.0/lib/web_console/templates/index.html.erb (124.8ms)

当你遇到这样的错误时,你应该总是转储实际的响应,以便你可以检查它。事实上,你得到一个 nil 的错误,代码做类似 ['data']['children'] 的事情意味着我猜你得到了一个 JSON 响应,但是缺少第一个项目之一(例如 ['data'] 返回零)。

不要假设每个请求都成功,很多事情都会导致 HTTP 失败。您可能会收到有效的 JSON 响应,而不是您期望的响应,例如一条错误消息 告诉您问题 .

此外,即使有 10 秒的延迟,您也可能会达到速率限制(从未亲自测试过 Reddit),但请阅读 rules

Many default User-Agents (like "Python/urllib" or "Java") are drastically limited to encourage unique and descriptive user-agent strings.

创建客户端 类 是使用 httparty 的更好方法:

class RedditClient
  include HTTParty

  format :json

  base_uri "http://reddit.com/r/"

  def self.get_category(category, *opts) 
    opts.reverse_merge(limit: 25)
    get("/#{category}.json", opts)
  end
end

HTTParty 以这种方式为我们处理 JSON 解析,并且不会尝试转换空响应。单独测试也容易得多。

但是您仍然应该在尝试使用之前检查响应是否成功:

@response = RedditClient.get_category(category)
if @response.success?
  attrs = @response['data']['children'].map do |child|
    {
       category: category,
       link: child['data']['url'],
       title: child['data']['title']
    }
  end
  Pic.create!(attrs)
else
  # log it or raise some sort of error
end

请注意,您将包含单个散列的数组传递给 .create。您可以改为传递一个哈希数组,它将在单个 SQL 插入语句中插入记录。

这种错误在 ruby 或 rails 中最常见。可以通过多种方式处理。正如@Stefan 建议的那样,您可以使用以下任何一种。

最简单的就是这样

response = HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')
if response.success?
  response_body = response.body
  # continue
end

response = HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')

case response.code
  when 200
    puts "Good!"
    # Continue your parsing
  when 404
    puts "NOT FOUND!"
  when 500...600
    puts "ERROR #{response.code}"
end

begin
   HTTParty.get('http://reddit.com/r/#{category}/.json?limit=25')
 rescue HTTParty::Error
   # HTTParty errors like Not found
 rescue StandardError
   # StandardError like Timeout
 else
   # continue
 end