【问题标题】:"undefined method []" when parsing reddit api解析 reddit api 时出现“未定义的方法 []”
【发布时间】:2018-02-12 12:40:31
【问题描述】:

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

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)

【问题讨论】:

  • 您正在进行 HTTP 调用。您很可能在 Reddit 遇到了速率限制或任何其他服务错误,因为 @response['data'] 为 nil,这引发了上述异常。检查@response['data']@response['data']['children'] 的http 响应/存在,以使您的脚本高效。
  • 不要盲目假设HTTParty.get(...) 是成功的。将其分配给变量response = HTTParty.get(...),并在处理其数据之前检查response.success?response.code

标签: ruby-on-rails json ruby reddit httparty


【解决方案1】:

当您遇到此类错误时,应始终转储实际响应,以便进行检查。事实上,nil 的代码执行['data']['children'] 之类的代码时出现错误,这意味着我猜你收到了 JSON 响应,但其中缺少第一项(例如 ['data'] 返回 nil)。

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

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

许多默认的用户代理(如“Python/urllib”或“Java”)都受到严格限制,以鼓励使用独特和描述性的用户代理字符串。

【讨论】:

  • 这是问题孩子。通过为每个请求使用不同的用户代理,问题得到了解决。我发现这个 gem 很有帮助:github.com/asconix/user-agent-randomizer 非常感谢 Fire Lancer!
  • 真的,你应该按照他们的要求去做。如果您造成问题,他们可能会对您采取更严厉的措施。 “更改您客户的用户代理 ...您的用户名作为联系信息,格式如下:<platform>:<app ID>:<version string> (by /u/<reddit username>)”。我不认为它需要是随机的,而且乍一看他们不希望它是。
【解决方案2】:

创建客户端类是使用 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 插入语句中插入记录。

【讨论】:

    【解决方案3】:

    这种错误在 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
    

    【讨论】:

      猜你喜欢
      • 2018-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-02
      • 1970-01-01
      • 2014-09-18
      • 2013-07-04
      • 1970-01-01
      相关资源
      最近更新 更多