FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

Akka Schedulerの定期実行を使って自動的にInstagramから画像を取得し、Tumblrへ投稿してみる

こんにちは!村井と申します。
見出しの通りですが、Akka Schedulerの定期実行を使ってInstagramから画像を取得し、
Tumblrへ自動投稿をしたいと思います。

Akkaの設定

Akkaとは

そもそもAkkaですが、
ScalaJavaで非同期処理を実現するためのライブラリで、
Scalaの開発元であるTypesafe社が中心となって開発しています。
Akkaは軽量な並列処理のためのActorモデルをベースとしており、
分散/並列/耐障害性を持ったイベント駆動型アプリケーションを構築できます。

akka-quartz-schedulerというライブラリを使用すれば、
cronのような記述で指定時間に処理を実行することが出来ます。
今回、playframeworkにこのライブラリを入れて、
自動的にブログ(Tumblr)にInsta画像を貼っていきたいと思います。

  1. build.sbtに設定
libraryDependencies += "com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x"
  1. application.confに時間を設定
akka {
  quartz {
    defaultTimezone = "Asia/Tokyo"
    schedules {
      AutomationBlog {
        description = "3時間ごとに実行"
        expression = "0 0 0-23/3 ? * *"
      }
    }
  }
}

そんなに頻繁に動くものを考えていないので、3時間に1回のスケジュールにしています。
設定方法はほぼcronと一緒ですが、こちらを参考にどうぞ。

  1. Actorを用意
    簡単に言えば、定期的に実行したい処理の部分ですね。
    今回はInstagramから画像を取得して、Tumblrに投稿することがそれに当たります。
class GetInstagramImageCreateTumblr extends Actor {

  def receive = {
    case msg: String =>
      val instaImgUrl = GetHashTagImage
      val tumblrResponce = CreateBlog(instaImgUrl)
  }
}

長ったらしいファイル名にしてますが、スルーでw
GetHashTagImageはInstaから画像を取得しており、
CreateBlog(instaImgUrl)で画像URLをTumblrに投稿しています。

  1. Globalオブジェクトを用意
object Global extends GlobalSettings {

  val system = ActorSystem("SampleSystem")
  val actor = system.actorOf(Props(classOf[GetInstagramImageCreateTumblr]))

  override def onStart(app: Application) = {
    QuartzSchedulerExtension(system).schedule("AutomationBlog", actor, "")
  }

  override def onStop(app: Application) = {
    system.shutdown()
  }
}

GlobalオブジェクトのonStartで定期実行の処理を開始させます。
AutomationBlogとありますが、これはapplication.confで指定したスケジュール名と一致させる必要があります。

これで設定は完了です。
activator runをすれば、GlobalのonStartでスケジューラーが動作します。

InstagramAPI

  1. アプリ登録とアクセストークンの取得
    さて、Instagramから画像を取得します。
    それにはまずAPIを叩きますが、そのためにアプリ登録してアクセストークンが必要になります。
    その辺は説明が長くなりすぎるので、
    こちらのアプリ登録アクセストークンの取得を参考にしてください。

  2. 画像の取得
    Instagramからの画像取得は指定したハッシュタグ画像を1つ取得しようと思います。
    今回使用したAPIこちらです。

private def GetHashTagImage = {
  // 取得したいハッシュタグを指定
  val hashTag = "cat"

  // 画像取得用のAPI(countで取得したい数を指定)
  val apiUrl = "https://api.instagram.com/v1/" +
    "tags/" + hashTag + "/media/recent?count=1&access_token=" +
    "**************************************************"

  // WSを使用してリクエストを投げてjsonで受け取る
  val apiResult = WS.url(apiUrl).get().map {
    response => response.json
  }
  val json = Await.result(apiResult, Duration.Inf)

  // jsonツリーを解析して画像URLだけ取得
  val imageUrl = json \ "data" \\ "low_resolution" map (_ \ "url")
  val imageUrlStr = imageUrl.map { a =>
    a.get.toString()
  }
  // ダブルクォーテーションが含まれてしまうので置換で削除しておく
  imageUrlStr.head.replace("\"", "")
}

一部のみですが、Instaからは以下ような感じでjsonが返ってきます。
今回は画像URLさえあればいいので、imagesのlow_resolutionのurlを取得しています。

"images": {
    "low_resolution": {
        "height": 320, 
        "url": "https://scontent.cdninstagram.com/hphotos-xtf1/t51.2885-15/s320x320/e35/12301169_1658230961121023_88910125_n.jpg", 
        "width": 320
    }, 
    "standard_resolution": {
        "height": 640, 
        "url": "https://scontent.cdninstagram.com/hphotos-xtf1/t51.2885-15/s640x640/sh0.08/e35/12301169_1658230961121023_88910125_n.jpg", 
        "width": 640
    }, 
    "thumbnail": {
        "height": 150, 
        "url": "https://scontent.cdninstagram.com/hphotos-xtf1/t51.2885-15/s150x150/e35/12301169_1658230961121023_88910125_n.jpg", 
        "width": 150
    }
}, 

TumblrAPI

Instaから画像は取得できました。
あとはTumblrに投稿するだけです。

  1. アプリ登録
    Instagram同様アプリ登録が必要です。
    詳しくはこちら

  2. OAuth認証
    今回使用するTumblrAPIはこちらのPhoto Postsです。
    投稿はGETメソッドではなくPOSTですね。OAuth認証が必要です。

  3. OAuth token取得
    TumblrInstagramTwitterみたいにブラウザなどからtokenを確認する方法はなく、
    token取得用プログラムを叩いて取得してね。となってるそうです。
    めんどくさいことこの上ないです。
    取得用プログラムを公開してる方もいたのですが、API仕様が変わったようで使えなくなってました。
    ですが!
    TumblrApi consoleにログインしてみると、
    サンプルプログラムが書かれている箇所にtokenが載っています!
    なので、これをコピペして使います。

  4. Jumblr
    Api consoleのサンプルプログラムの中に、Javaがあります。
    JavaではTumblrOAuth認証のためにJumblrが提供されています。
    このライブラリを使用せずにscalaだけでやっちゃおうかと思ったのですが、
    せっかくなのでJumblrを使用することにしました。

  5. build.sbtにJumblrを設定

libraryDependencies += "com.tumblr" % "jumblr" % "0.0.11"

こちらMaven Repositoryで最新版を確認して設定してください。

  1. 投稿する
private def CreateBlog(imageUrl: String) = {
  val consumerKey = "**************************************************"
  val consumerSecret = "**************************************************"

  val client: JumblrClient = new JumblrClient(
    consumerKey,
    consumerSecret
  )
  client.setToken(
    // oauthToken
    "**************************************************",
    // oauthTokenSecret
    "**************************************************"
  )

  // postCreateのparamはjava.util.Mapなので、scalaのMapではなくこちらを使用する
  val params: java.util.Map[String, String] = new HashMap[String, String]()
  params.put("type", "photo")
  params.put("source", imageUrl)
  client.postCreate("instagramtagimage.tumblr.com", params)
}

Jumblrのおかげでめっちゃ簡単ですね。
imageUrlはInstaから取得した画像URLを渡しています。
consumerKey
consumerSecret
oauthToken
oauthTokenSecret
は4に記載している通り、TumblrApi consoleに記載されているのでコピペしてください。

完成

Tumblrアプリ登録時に作っておいたブログを見てみましょう。
f:id:y_muraix:20151204154632p:plain ちゃんと投稿されてますね。

まとめ

Akka schedulerを使わなくてもcronでいいやん。って思われますが、
プログラム含め、Playひとつで完結出来るんで管理もしやすいですね。

他にもTwitterに自動的につぶやいたり、
画像をクローリングして、Pinterestにピンしておいたり、など色々出来ますね。
もちろん、SNS以外にも使えますw

長くなりましたが以上です。
最後まで読んでいただきありがとうございました。