Processing で Web API (Web サービス) を利用するプログラムを作る

目次

Web API / Web サービスとは

Web API (Web サービス)とは、様々なWebサイトで提供されている情報を自分のプログラムに読み込んで利用する仕組みです。 天気予報・地図・商品情報・SNSへの投稿など多様な情報が手に入ります。 多くの場合、使用回数の制限(たとえば1日何回まで)があったり、事前の利用登録が必要だったりしますが、ここでは利用登録なしで無制限に利用できる手軽なものを取り上げます。

JSON 形式の Web API を利用する

livedoor お天気Webサービス を利用して天気予報情報を取得・表示してみます。このサービスが提供する http://weather.livedoor.com/forecast/webservice/json/v1?city=280010 のようなJSON形式のデータは Processing では loadJSONObject 関数で読み込むことができます。

String baseURL = "http://weather.livedoor.com/forecast/webservice/json/v1?city=";
String city = "280010";
String title;
JSONArray forecasts;
void setup(){
  size(200, 200);
  JSONObject w = loadJSONObject(baseURL + city);
  title = w.getString("title");
  forecasts = w.getJSONArray("forecasts");
}
void draw(){
  background(255);
  fill(0);
  text(title, 10, 30);
  for(int i = 0; i < forecasts.size(); i++){
    JSONObject f = forecasts.getJSONObject(i);
    String s = f.getString("dateLabel") + ":" + f.getString("telop");
    JSONObject t = f.getJSONObject("temperature");
    if(!t.isNull("min")){
      String d = t.getJSONObject("min").getString("celsius");
      s = s + ", min:" + d;
    }
    if(!t.isNull("max")){
      String d = t.getJSONObject("max").getString("celsius");
      s = s + ", max:" + d;
    }
    
    text(s, 10, 50 + 20 * i);
  }
}

応用

このようなデータがあれば、自分好みの天気予報アプリや天気によって見た目や難易度が変わるゲームを作ることなどができるようになります。

XML 形式の Web API を利用する

はてなブックマーク の各種RSSフィード (新着情報をXML形式で配布する一般的な仕組み) のうち、ホットエントリーを取得・表示してみます。 http://feeds.feedburner.com/hatena/b/hotentry にあるようなXML形式のデータは Processing では loadXML 関数で読み込むことができます。

String url = "http://feeds.feedburner.com/hatena/b/hotentry";
XML[] items;
float y;
void setup(){
  size(400, 400);
  XML xml = loadXML(url);
  items = xml.getChildren("item");
  y = 20;
}
void draw(){
  background(0);
  fill(255);
  
  for(int i = 0; i < items.length; i++){
    String title = items[i].getChild("title").getContent();
    String subject = items[i].getChild("dc:subject").getContent();
    text((i + 1) + ":[" + subject + "] " + title, 10, y + 20 * i);
  }
}
void mouseDragged(){ // Scroll the list by dragging 
  y += mouseY - pmouseY; 
}

応用

このようなデータがあれば、自分好みのはてなブックマーク閲覧アプリ、今流行している単語が出題されるクイズゲームやタイピングゲームなどを作ることができるようになります。

Twitter API を利用する

Twitter API を利用すると投稿などを取得することができます。上で紹介している API と違って登録が必要なので少し面倒です。

アカウントの準備

Twitter API を利用するためには「電話番号が登録されている Twitter のアカウント」が必要なので、アカウントがない人は作成し、電話番号を登録していない人は登録してください。 電話番号は Twitter の設定画面から登録できます。(2016年7月現在、Webからだと「モバイル」、アプリからだと「アカウント」の中にあります。)

アプリの登録~アクセストークンの作成

https://apps.twitter.com/ にアクセスして、アプリを登録します。 "Create New App" ボタンを押して表示される画面に必要な情報を入力してください。 アプリの名前、説明(10文字以上)、ウェブサイトの項目が必須ですが、後から変更することもできますし、最初は適当に入力しておいても大丈夫です。

無事にアプリが登録できたらアプリの設定画面が表示されますので、続いて「アクセストークン」を作成します。 "Keys and Access Tokens" をクリックして、下部の "Create my access token" ボタンを押すとアクセストークンが作成されます。 そのページ中に表示されている以下の4つの文字列を後で使いますので手元にコピーしておきましょう。

これらの情報は秘密にしておきましょう。プログラムを公開したりするときは、その部分を削除するなどしてください。

Twitter 4J の導入

Twitter API を直接利用するのは少し面倒なので、 Twitter4J というライブラリを利用するのがオススメです。

  1. 最新安定バージョンというところにある twitter-4j-4.0.4.zip をダウンロードしてください (バージョン番号は2016年7月現在確認)
  2. ダウンロードした zip ファイルを展開してください
  3. 中身に lib という名前のフォルダを、下のようなファイル構成になるように [Processingスケッチブックの場所]/libraries の中に移動してください
  4. Processing を起動している場合は、一度終了させて、再度起動してください
  5. Processing のメニューのスケッチ>ライブラリのインポートの中に Twitter4J があれば成功です
Processing
- libraries
  - Twitter4J
    - library (lib じゃだめなので要注意)
      - twitter4j.jar (twitter4j-core-4.0.4.jar の名前を変更する必要があるので要注意)
      - twitter4j-async-4.0.4.jar
      - twitter4j-examples-4.0.4.jar
      - twitter4j-media-support-4.0.4.jar
      - twitter4j-stream-4.0.4.jar
- (libraries と同じ階層には自分が Processing で作成したプログラムのフォルダが並んでいるはずです)

プログラムの作成

…ようやく本題に入ることができます。まずはライブラリのインポートから Twitter4J を選択してください。 import から始まる行がたくさん挿入されます。その続きに自分のプログラムを書いていきましょう。

タイムラインの受信、投稿

以下のサンプルでは、setup 関数で Twitter に接続し、 mouseClicked でタイムラインの取得、keyTyped で投稿しています。 draw では取得したツイートのユーザ画像を並べて表示しています。 画像にマウスカーソルを合わせるとそのツイートの内容が表示されます。

Twitter4J では1つの投稿を Status クラスのオブジェクトとしています。 これをそのまま用いてもいいのですが、ツイートの取得時にプロファイル画像を読み込んでそれを変数に保持しておきたいので、ここではそのための Tweet というクラスを作成して利用しています。

getHomeTimeline がタイムラインを取得する関数です。結果は、より新しいツイートが先頭に来るようなリストになります。引数なしで呼び出すと毎回最新20ツイートを取得します。 2回目以降は前回までに取得したツイートより後に投稿されたツイートだけを取得すればいいので、「これより後のツイートだけください」という情報を引数の Paging で指定しています 。 Paging を利用することで逆に最新20件よりも古いツイートを取得したり、一度に20件以上のツイートを取得したりすることが可能です(詳細はこちら)。

updateStatus が投稿するための関数です。

import twitter4j.*;
import twitter4j.api.*;
import twitter4j.auth.*;
import twitter4j.conf.*;
import twitter4j.json.*;
import twitter4j.management.*;
import twitter4j.util.*;
import twitter4j.util.function.*;

String consumerKey = "該当する文字列をコピペ";
String consumerSecret = "該当する文字列をコピペ";
String accessToken = "該当する文字列をコピペ";
String accessTokenSecret = "該当する文字列をコピペ";

class Tweet {
  long id;
  String text;
  String name;
  String screenName;
  PImage profileImage;
  public Tweet(Status s) {
    id = s.getId();
    text = s.getText();    
    User u = s.getUser();
    name = u.getName();
    screenName = u.getScreenName();
    profileImage = loadImage(u.getProfileImageURL());
  }
}

Twitter twitter;
ArrayList<Tweet> tweets;

void setup() {
  size(400, 400);
  tweets = new ArrayList();
  ConfigurationBuilder builder = new ConfigurationBuilder();
  builder.setOAuthConsumerKey(consumerKey);
  builder.setOAuthConsumerSecret(consumerSecret);
  builder.setOAuthAccessToken(accessToken);
  builder.setOAuthAccessTokenSecret(accessTokenSecret);
  TwitterFactory factory = new TwitterFactory(builder.build());
  twitter = factory.getInstance();
}

void draw() {
  background(255);
  fill(0);
  String pointed = null;
  for (int i = 0; i < tweets.size(); i++) {
    Tweet t = tweets.get(i);
    int n = height / t.profileImage.height;
    float x = i / n * t.profileImage.width;
    float y = i % n * t.profileImage.height;
    image(t.profileImage, x, y);
    if (x < mouseX && mouseX < x + t.profileImage.width && y < mouseY && mouseY < y + t.profileImage.height) {
      pointed = t.name + " @" + t.screenName + "\n" + t.text;
    }
  }
  if (pointed != null) {
    text(pointed, mouseX, mouseY, 200, 200);
  }
}

void mouseClicked() {
  updateTimeLine();
}

void keyPressed() {
  tweet("Tweeting from my program in Processing!");
}

void updateTimeLine() {
  try {
    ResponseList<Status> result;
    if (tweets.size() > 0) {
      Paging p = new Paging(tweets.get(0).id);
      result = twitter.getHomeTimeline(p);
    } else {
      result = twitter.getHomeTimeline();
    }
    ArrayList<Tweet> temp = new ArrayList();
    for (int i = 0; i < result.size(); i++) {
      temp.add(new Tweet(result.get(i)));
    }
    tweets.addAll(0, temp);
  }
  catch(TwitterException e) {
    println("Error occured while trying to get the timeline.");
  }
}

void tweet(String text) {
  try {
    twitter.updateStatus(text);
  }
  catch(TwitterException e) {
    println("Error occured while trying to tweet.");
  }
}

Twitter API には他にもたくさんの機能があります。 詳しくは Twitter4J のコード例ドキュメント を見てください

TwitterStream を利用する

上の例ではプログラムからツイートをくださいとリクエストした (たとえば getHomeTimelineを実行した) ときにツイートが取得できましたが、これではリアルタイムにツイートを受信することができません。 Twitter APIの関数は全部合わせて15分間あたり180回までの回数制限があるからです。(といっても5秒に1回くらいはいけるわけですが)

TwitterStream を利用すると、条件を満たす新しいツイートが投稿されるたびに即座に受信されます。 以下の例では、指定した文字列での検索結果をリアルタイムに受信しています。

// import から class Tweet までは上の例と同じなので省略

ArrayList<Tweet> tweets;

void setup() {
  size(400, 400);
  tweets = new ArrayList();
  ConfigurationBuilder builder = new ConfigurationBuilder();
  builder.setOAuthConsumerKey(consumerKey);
  builder.setOAuthConsumerSecret(consumerSecret);
  builder.setOAuthAccessToken(accessToken);
  builder.setOAuthAccessTokenSecret(accessTokenSecret);
  TwitterStreamFactory factory = new TwitterStreamFactory(builder.build());
  TwitterStream stream = factory.getInstance();
  stream.addListener(new MyStatusListener());
  stream.filter("検索したい文字列");
}

void draw() {
  background(255);
  fill(0);
  float th = textAscent() + textDescent();
  float margin = 12;
  text("Received " + tweets.size() + " tweets", margin, th);
  if (tweets.size() > 0) {
    Tweet t = tweets.get(tweets.size() - 1); // latest tweet
    text("Latest tweet:", margin, th * 2);
    image(t.profileImage, margin, th * 3);
    float tx = t.profileImage.width + margin * 2;
    float tw = width - t.profileImage.width - margin * 3;
    text(t.name + " @" + t.screenName, tx, th * 3);
    text(t.text, tx, th * 4, tw, height - th * 4);
  }
}

class MyStatusListener extends StatusAdapter {
  public void onStatus(Status s) {
    tweets.add(new Tweet(s));
  }
}

その他の Web サービス

事前登録や認証が必要になるのでやや難しいですが、挑戦してみてください。