【Ruby on Rails】instagram apiを利用してoauth認証・タイムラインを表示する

2018年9月21日Rubyapi, Instagram, Ruby, Ruby on Rails, タイムライン

週末プログラミングの時間がやってまいりました。

現在、僕は各種SNSのタイムラインをログに残すというサービスを開発中なんですが、その一部をご紹介します。

前提としてfacebookに買収されたinstagramですが、広告配信を開始したりとアドテクの分野でも話題に上がるようになってきており、無視できない媒体になっています。(instagramがなぜ広告モデルを始めたのか、それは別ページでご紹介させていただきます。)

instagramのapiを弄って利用したいと考えているウェブ系の方は多いと思います。

ただし、最近apiの実装が可能になったこともありまだまだ検索しても情報は少ないです。

僕が行っている実装方法等をまとめます。(oauth認証からタイムライン取得まで。)

ちなみに、instagram api自体は非常に取り扱いやすい仕様になっていて、好感が持てます。facebook apiはイマイチ使いづらいような気がするのですが、僕だけでしょうか。

instagram apiを使ってタイムラインを表示してみよう

Railsの場合、便利なgemファイルがあるので、以下を入れておきましょう。(Rails 4.2.4, ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15])

Gemfileの中身

## instagram api 関連##
gem 'instagram'

## http関連。サーバー側とrest fulにやり取り可能。 ##
gem 'rest-client'

## uri関連。api叩いて受け取るデータ形式がjsonなので。 ##
gem 'json'

コメントに書いてある通りですが、説明を。

どのSNS apiでもそうですが、所定のURLにpostでアクセストークンなどを載せてリクエストを送ればjsonが返ってくるという仕様になっています。

なので、上記gemを導入しているとRailsの場合実装が非常に簡単になります。

instagram.rbを設定しておく(/config/initializers/instagram.rb)←名称はinstagram.rbにしないとダメ

require "instagram"

Instagram.configure do |config|
  config.client_id = "あなたのclient id"
  config.client_secret = "あなたのclient secret"
end

client id, client secretって何ぞ?という方も多いと思います。

こちらはinstagramの開発用アカウント(https://instagram.com/developer/)からアプリケーションを登録することで取得することができます。

リンク先を表示して、Manage Clientsのタブをクリックし、新規登録することで簡単に作成できます。

上記画面のclient idとclient secretを先ほどのinstagram.rbに貼り付けましょう。

routes.rbはこんな感じ(/config/routes.rb)

Rails.application.routes.draw do
  get 'instagram/timeline'

  root 'home#index'
  
  get "home/index"
  
  # instagram routes
  get "/auth/instagram" => "instagram#token"
  get "/auth/instagram/connect" => "instagram#get_token_timeline"
  get "/signout_instagram" => "sessions#destroy"
  
  # Oauth routes
  get "/auth/:provider/callback" => "sessions#create"
end

※Railsのrest fulなルーティング設定に関してはよく分かっていないので、もっと良い設定絶対あります。

上記設定は、instagram_controller.rbの中身も確認した上でご自身の設計に合わせて設定してください。

application.html.erbは適当に。(/view/layouts/application.html.erb)

<!DOCTYPE html>
<html>
<head>
  <title>SocialLog</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>

<body>
  <div id="container">
    <div id="user_nav">
      <%= link_to "instagramにサインイン", "/auth/instagram" %>
    </div>
    <%= yield %>
  </div>
</body>
</html>

リンク先URLは、/auth/instagramというpathで設定しましょう。

これはapiリファレンスを見れば分かりますが、instagram側でgetで上記pathにリクエストを送ることで、アクセストークン発行用のURlにリダイレクトされます。

ちなみにhtmlに関してはクソ適当です。気にしないでください。

以下でapiの処理部分の内容について記述したファイルの中身を見てみましょう。

instagram_controller.rbでほとんどの処理をさせる

class InstagramController < ApplicationController

  require "instagram"
  require "json"
  require "rest-client"

 ## access_token発行用のURLへのリダイレクト。instagra-ruby-gemのauthorize_urlメソッドを使っています。
  def token
    connect_url = "http://localhost:3000/auth/instagram/connect"
    redirect_to Instagram.authorize_url(:redirect_uri => connect_url) and return
  end
  
  ## postで所定のURLに情報を送信し、返ってきたjsonからaccess_tokenを取得し、タイムラインを表示する
  def get_token_timeline
    connect_url = "http://localhost:3000/auth/instagram/connect"

    ## 下記要素をpostで送ると、access_tokenを含んだjsonが返ってきます。
    res = RestClient.post 'https://api.instagram.com/oauth/access_token', {
      client_id: 'あなたのclient_id',
      client_secret: 'あなたのclient_secret',
      grant_type: 'authorization_code',
      redirect_uri: connect_url,
      code: params[:code]
    }
    
    json = JSON.parse(res.body)
    token = json['access_token']
    
    ## jsonにはuser_idのデータも含まれます。
    ## user_id = json['user']['id'] でuser_idを取得すると、該当するユーザーのタイムラインを取得できます。
    ## 今は自分のタイムラインを表示するので使いません。
    
    ## タイムラインの取得には、下記URLにgetでリクエストを送りましょう。
    feed_url = "https://api.instagram.com/v1/users/self/feed?access_token="
    
    ## タイムラインの投稿取得数を指定できます。上限100のはず。
    count = "&count=100"
    get_feed_url = feed_url + token + count
    uri = URI.parse(get_feed_url)
    http = Net::HTTP.new(uri.host, uri.port)

    ## ここ落とし穴なんですが、instagram apiはSSL通信を前提としているので、httpsで通信するように記述しないとjsonデータが返ってきません。
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    req = Net::HTTP::Get.new(uri.request_uri)
    res = http.request(req)

    ## 後はjsonのデータを取得するだけ。
    feed_json = JSON.parse(res.body)
    puts feed_json    
  end
end

本当はdeviseを用いたoauth認証にするのがイケてると思いますが、僕の開発中のサービスは都度タイムラインをapi叩いて取得するため、deviseを導入する意味がありません。

結果、sessionにaccess_tokenをぶち込むという謎設計になっています。。

sessions_controller.rbも一応作っておいた。

class SessionsController < ApplicationController
  def create

    if(request.path_info == "/auth/instagram/callback")
      auth = request.env["omniauth.auth"]
      session[:instagram_oauth_token] = auth.credentials.token
      redirect_to "/instagram", :notice => "サインイン!"
    else
      redirect_to root_url
    end
  end

  def destroy
    session[:instagram_oauth_token] = nil
    redirect_to root_url, :notice => "サインアウト!"
  end
end

これは開発中のサービスの状況に合わせて柔軟に設定変更してください。

そんなに謎な実装をせずに、自分のタイムラインを表示させることができます。

ちなみに二次元配列(連想配列)に投稿の要素を格納したいのですが、知識不足でイマイチ分かっておらず・・分かり次第更新します。

ここまでお読みいただき、ありがとうございます!

未経験の僕がRuby on Railsをここまで扱えるようになったのは「TechAcademy」のおかげです。

僕はアドテクに関するお仕事をしていますが、学生時代の専攻が情報系であったこともありません。

しかし、ここまでコードを書けるようになったのは「TechAcademy」というプログラミングスクールのカリキュラム取り組んだおかげです。

気になる方はぜひ、以下記事を御覧くださいね。

2018年9月21日Rubyapi, Instagram, Ruby, Ruby on Rails, タイムライン

Posted by reviewer