Ba.とWEBと梅干し太郎

WEB制作・プログラミング・音楽の学習したこと・つくったもののアウトプットブログです。あとは日記。

クリスマス後のカップルの様子をTwitterで分析するクソアプリを作った with @t_yng

どうも!
もっこりMokkoです!

クリスマスもいよいよ終わりを迎えるこのタイミング。 クソアプリアドカレ2016の大トリを投稿させていだきます。

#作ったもの
f:id:masayannuu:20161226001655p:plain
ゲスッター
※予告なく公開停止にする場合があります。

今回はフロントとデプロイ周りを私が、サーバー側を@t_yng氏で分担して作りました。
ありがとう、t_yng氏!

どんなアプリかは多くを語ることなかれ、みてくださいな。
ゲスッターはこちら

クリスマス前後で別れたカップル多すぎてビビりました。

いつまで動かしとくか分からないけど、twitterがある限りゲスッターにもカップルカウントとツイートは溜まり続けますのでたまに覗くと面白いかも?

#何をしているの?
Twitterのエゴサで特定のワードを含んでいるものを取得し、カウントしています。
「何でエゴサすれば目的の情報がとれるか?」を考えるのは面白かったです。
なお、ゲスッターはあくまでクソアプリなので、検索ロジックはゆるめです。

#中はこうなっている
TwitterAPIを定期的に叩いて、fireBaseのDBに入れておき、それをJSから呼び出しています。
フロントはt_yng氏に教えてもらったNetlifyという静的フィアルのホスティングサービスを使って公開しました。めちゃくちゃ便利なので気になる人は一度触って見て欲しい。

というわけで、緩めですがクソアプリ2016締めの投稿でございました! 思いの外良い勉強になりましたし、作るの楽しかったです。

それでは、良いお年を!

(JAVA) Play FrameworkでAkkaのスケジューラーを使わずに処理を定期実行する

どうも!もっこりmokkoです。

これはHamee Advent Calendar 2016の24日目の記事です!

前回に引き続きPlay FrameWorkネタを一つ。
Play FrameworkではAkkaを使用したスケジューラーが提供&推奨されておりますが、今回はjarファイルを作成しcrontabで定期実行する方法です。
フレームワークの思想からは外れるかもしれないけど、WEBアプリケーションのデプロイ時にバッチの動作を担保したい場合は有用かも。あくまで一つの方法として記事にしておきます。


#この記事から分かること
- PlayFrameWorkでjarファイルを作成して処理を定期実行をする方法
- jarファイルの生成に使用するプラグインの導入方法


#作ったもの
- 10分間に一回、引数に渡した文字列でtwitterでエゴサしてコンソールに出す

#jarファイルを作成するプラグインを導入
sbt assemblyというプラグインを使用します。 Play FarmeworkはScalaで実装されたフレームワークなのでパッケージ管理はsbt(Scala build tool)になります。
というわけで、project/plugins.sbtに下記を追記。

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")

せっかくなので最新バージョン(2016年12月末時点)を使用。
過去のバージョンに比べて設定に必要な記述が減っています。

続いてsbt assemblyの設定をbuild.sbtに記述します。
今回はシンプルに3つの設定だけ書きました。

//jarファイルの名前を決める。  
assemblyJarName in assembly := "twitter-kun.jar"  
//これを書いておくと、テストをスキップしてjarファイルを作成できる。コンパイル時間が長いので今回はスキップしちゃいます。    
test in assembly := {}  
//jarファイルのマニュフェストを設定できます。今回は実行したいmain関数を持つクラスを指定しました。  
mainClass in assembly := Some("batch.GetToTweet")

これでjarファイルを作成する準備は整いました。
プラグインをインストールする時間がかかるので、すぐにassemblyコマンドを叩くとエラーになるかも。

実際に叩くコマンドはこちら。プロジェクト直下で叩くとtarget/scala-x.xx.xx/下にjarファイルができます。

$activator assembly  

後はcrontabに書いてjarを実行してあげればいいわけですが、ここでハマりポイントがあります。
Play Frameworkはサーバー上でアプリケーションが起動している状態じゃないとクラスやORMを使えない場合があります。
Ebeanを使った時はうんともすんとも言いませんでした。

というわけで、jarファイルからでもアプリのソースを使いまわせるよう実行するmain関数内でアプリケーションの起動と停止をやってあげましょう。
Play Framework入門の記事で作ったアプリに定期実行で使えるクラスを追加してみました。

package batch;

import controllers.ApplicationController;
import play.api.Play;
import play.inject.guice.GuiceApplicationLoader;
import play.libs.Json;
import twitter4j.TwitterException;
import utils.TwitterSearch;
import java.io.File;
import java.util.Arrays;

public class GetToTweet {
    public static void main(String[] args) {

        //アプリケーションを起動するソース
        play.Environment env = new play.Environment(
                new File("."),
                ApplicationController.class.getClassLoader(),
                play.Mode.PROD
        );
        play.ApplicationLoader.Context context = play.ApplicationLoader.Context.create(env);
        GuiceApplicationLoader appLoader = new GuiceApplicationLoader();
        play.Application application = appLoader.load(context);

        //javaのライブラリにはstartのメソッドがないため、ここだけScalaのライブラリのPlay.api.play
        Play.start(application.getWrappedApplication());

        //コンソールに取得したツイートをはく。
        Arrays.stream(args).forEach(arg -> {
            try {
                System.out.println(Json.toJson(TwitterSearch.searchTweet(arg).getTweets()));
            } catch (TwitterException e) {
                e.printStackTrace();
            }
        });

        //処理が終わったらアプリケーションを停止してあげることも忘れずに
        Play.stop(application.getWrappedApplication());
    }
}

起動時に使っているクラス達はJavaScalaで同名のクラスがあり、importを間違えると型を解決できなくなるので要注意!
今回書いたソースでは、Play.start()までは全部Javaのクラスを使います。

        //scalaにはplay.api.Environmentがある。play.apiと続くものはscala。
        play.Environment env = new play.Environment(
                new File("."),
                ApplicationController.class.getClassLoader(),
                play.Mode.PROD
        );
        play.ApplicationLoader.Context context = play.ApplicationLoader.Context.create(env);
        GuiceApplicationLoader appLoader = new GuiceApplicationLoader();
        play.Application application = appLoader.load(context);

        //javaのライブラリにはstartのメソッドがないため、ここだけScalaのライブラリのPlay.api.play。Applicationクラスの.getWrappedApplication()でscalaのアプリとしてstart()に渡さなければいけない。
        Play.start(application.getWrappedApplication());

これで問題なくjarファイルが実行できるようになりました!

java -jar twitter-kun.jar 'メリークリスマス'  

f:id:masayannuu:20161225142002p:plain

Jsonにして出力してみました。
play.libs.Jsonを使えば簡単にListをJsonにして出力できるので便利です。

あとはcrontabをこんな感じで書けば、定期実行できますね。

*/10 * * * * root java -jar hogehoge/twitter-kun.jar 'メリークリスマス'   

といったところで、javaでplay Framework使っている人のお役に立てれば幸いです!

(Java) Play FrameWork入門! Twitter のつぶやき検索アプリを作ってみたよ

どうも!もっこりmokkoです。

日付も変わろうとしていますが、Hamee Advent Calendar 2016の10日目の記事です!
初アドカレにドキがムネムネなわけですが、最初書こうと思っていた内容(スクレイピングと置換)は自分自身納得ができなかったのでまた次回に。

というわけで、今日はScalaフレームワークとして有名な「Play Framework」のJava版入門を1つ。
簡単なTwitterのエゴサアプリを作ってみました。

デモ
※herokuのFreeの使用時間超えて止まっちゃいました。(12月11日追記)


この記事から分かること
- PlayFrameWorkの導入方法
- 簡単なアプリケーションのつくりかた
- 開発環境 & 本番環境での動かし方
(組み込みのNetty と Tomacatの2手法を紹介)


Play FrameWorkとは

The High Velocity Web Framework For Java and Scala

公式ドキュメントより引用
https://www.playframework.com/

Ruby on Rails感覚で扱えるJavaフレームワーク
Scala界ではかなり有名だそう。

導入方法
公式ページよりactivatorというツールをインストール。
▼こちらからどぞ
Typesafe Activator(https://www.lightbend.com/activator/download)

コマンドラインを通して、コマンドを叩けばすぐ使える。

▼ちょっとサボるけど、詳しくはこちらを参照
Installing - 2.4.x

プロジェクトを作成するコマンドはこれ。

$ activator new 

対話式でアプリ名など色々聞かれるので答えると必要なファイルが生成される。

Javaアプリを作成する場合はplay-javaを選択

Browse the list of templates: http://lightbend.com/activator/templates
Choose from these featured templates or enter a template name:
  1) minimal-akka-java-seed
  2) minimal-akka-scala-seed
  3) minimal-java
  4) minimal-scala
  5) play-java
  6) play-scala
(hit tab to see a list of all templates)

簡単なアプリケーションを作ってみた
機能はtwitterでエゴサしてそれを引っ張ってくるだけ。

※Play Frameworkのバージョンは2.4.8を使用。現時点での最新バージョンは2.5.10。

▼画面はこんなの
f:id:masayannuu:20161210234432p:plain

コマンドでプロジェクトを作成すると既に Model 、View、Controllerのクラスが作成されている。
このMVCの仕様はjava初めての私でもとても分かりやすかった。

▼Modelクラスはこんな感じ。

package models;

public class SearchWord {

    public String word;

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}

今回はPOSTされた文字列をtwitterでエゴサするだけなのでシンプルに。
DBに繋がないのでORMも使用しない。
Ebeanを使用した時はebeanのModelクラスを継承して使った。

▼そして、conf/routesにルーティングを書く。

#アプリのルーティングを設定するファイル  
#HTTPリクエスト  URL   呼び出すメソッド名 の順に記載  
GET     /                           controllers.ApplicationController.index()

POST    /search                     controllers.ApplicationController.search()

# 静的ファイルのルーティング viewの呼び出しに使用する
GET     /assets/*file               controllers.Assets.at(path="/public", file)

こんな感じで、アクセスしたURLに応じて呼び出すControllerを指定する。
分かりやすい。

▼Controllerクラス

package controllers;

import models.SearchWord;
import play.data.Form;
import play.mvc.Controller;
import play.mvc.Result;
import twitter4j.TwitterException;
import utils.TwitterSearch;

import java.util.List;
import java.util.Map;
public class ApplicationController extends Controller {

    /** POSTされた文字を元にtwitterでエゴサをかけるよ*/
    public Result search() throws TwitterException {
        Map<String, String> postData = new Form(SearchWord.class).bindFromRequest().data();
        List<twitter4j.Status> searchResult =  TwitterSearch.searchTweet(
                postData.get("searchWord"))
                .getTweets();

        return ok(views.html.listTweet.render(searchResult));
    }

    /** viewを表示するだけだよ */
    public Result index() {
        return ok(views.html.index.render());
    }

}

Resultクラスで受け取ってviewへレンダリング
POSTされた値を受け取る時はFormクラスを使えばMAP型で受け取れる。

twitterAPIを叩くライブラリはTwitter4Jを使わせて頂きます。

package utils;

import play.Play;
import twitter4j.*;
import twitter4j.conf.ConfigurationBuilder;

public class TwitterSearch {

    public static QueryResult searchTweet(String searchWord) throws TwitterException {

        ConfigurationBuilder configInfo = new ConfigurationBuilder();
        //application.confからTwitterの設定を読み込むよ
        configInfo.setDebugEnabled(true)
                .setOAuthConsumerKey(Play.application().configuration().getString("twitter.authKey"))
                .setOAuthConsumerSecret(Play.application().configuration().getString("twitter.authSecret"))
                .setOAuthAccessToken(Play.application().configuration().getString("twitter.authToken"))
                .setOAuthAccessTokenSecret(Play.application().configuration().getString("twitter.authSecretToken"));
        TwitterFactory tf = new TwitterFactory(configInfo.build());

        Twitter twitter = tf.getInstance();
        Query query = new Query();
        query.setQuery(searchWord);
        QueryResult result = twitter.search(query);

        return result;

    }

}

twitterの認証に使う情報はconf/application.confという設定ファイルに外出し。
このapplication.confという設定ファイルは環境変数から読み込めるのでおすすめ。

#twitterの認証情報を環境変数から取得  
twitter.authKey=${?AUTH_KEY}
twitter.authSecret=${?AUTH_SECRET}
twitter.authToken=${?AUTH_AUTHTOKEN}
twitter.authSecretToken=${?AUTH_SECRET_TOKEN}

#アプリの言語
play.i18n.langs = [ "ja" ]
 

▼他クラスでapplication.confの変数を呼ぶ時はこれ
(※Playのバージョンによって使うクラスが絶妙に違う。2.4はこれでいける)

Play.application().configuration().getString("twitter.authKey")

▼Viewクラス

@import b3.vertical.fieldConstructor

@main("ツイッターくん") {
    @b3.form(routes.ApplicationController.search, 'class -> "form-group") {
           <div class="col-sm-offset-0 col-sm-6">
                <label for="searchWord">#つぶやき検索</label>
                <input type="text" name="searchWord">
                @b3.submit('class -> "btn btn-default"){ <span class="glyphicon glyphicon-globe"></span> 検索}
           </div>
    }
}

Play Framewokのviewテンプレート。
ヘルパーでhtml生成はもちろん、素書きでもOKなので扱いやすい。
bootstrapにも対応しており、公式のPlay Bootstrapがあるのであまり手をかけず整ったページを作れる。

viewテンプレートの記法はScalaを使う。
@以降がScalaとして認識されてるのでせ、ループ処理もこのように書ける。

@(searchResult: List[twitter4j.Status])

@main("ツイッターくん") {
    @for(tweet <- searchResult) {
        <li>@tweet.getText()</li>
    }
}

分かりづらいのが、@main()なるもの。
これは要するに変数で、viewの共通ファイルになっているmain.scala.htmlというファイルに渡すhtml要素を定義している。

▼main.scala.htmlはこんな感じ
最上部に@()で定義されているのが、このファイルに渡ってくる引数。
他のviewの@main("ツイッターくん"){ hogehoge }の"ツイッターくん"がmain.scala.htmlのtitleとして、そして{hogehoge}がhtmlとして渡されている。

@(title: String)(content: Html)

<!DOCTYPE html>
<html>
    <head>
        <title>@title</title>
        <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript"></script>
    </head>
    <body>
        <div class="page-header">
            <a class="navbar-brand logo" href="/">
                <img src="@routes.Assets.at("images/medjed.png")" />
            </a>
            <h1>Play Frameworkのテストだよ
                <br>
                <small>つぶやきを検索するよ</small>
            </h1>
        </div>

        @content

    </body>
</html>

ささ、これだけでつぶやき取得アプリが完成!
テストと入力すると... f:id:masayannuu:20161211002329p:plain

つぶやきを取得する。
f:id:masayannuu:20161211002426p:plain

デプロイしてみる
組み込みサーバーで動かすのはactivatorのコマンドで

$ activator start

application.confでsecret key(クライアントセッションの暗号化に使う)を指定していない場合はstartの引数に

$activator start -Dapplication.secret=abcdefghijk

だけでOK。

▼組み込みサーバーでデプロイ時のコマンドについて。こちら参照
Production - 2.4.x

Tomcatでデプロイする場合はwarファイルを作成する必要があるので、 play2warというプラグインを入れてコマンド1つでサクッと。

project/plugin.sbtに

addSbtPlugin("com.github.play2war" % "play2-war-plugin" % "1.4-beta1")

と build.sbtに

import com.github.play2war.plugin._  

libraryDependencies ++= Seq(
  cache,
  jdbc,
  "mysql" % "mysql-connector-java" % "5.1.40",
  "com.adrianhurt" %% "play-bootstrap" % "1.1-P24-B3",
  "org.twitter4j" % "twitter4j-core" % "4.0.5"
)

このように依存関係を追加すればコマンドからwarファイルを作成できる。

コマンドは

$activator war

使っているplay frameworkのバージョンによってバージョンの指定が異なるので注意! 2016年12月時点で2.5以降は非対応。

後はこのwarファイルをTomcat直下のwebapps内に置いてTomcatを立ち上げればデプロイ完了。


使ってみた感想
java初心者にとってとっつきやすかった。
ハマりどころも多いが嫌いじゃないし、むしろ好きかも。

PS.
▼ライブラリ探しはmaven repositoryでどうぞ。
https://mvnrepository.com/

米国大統領選の週ごとの投票率をいい感じのデザインで見れる「Count Yourself In」

グラフィックインフォを見かけることが多くなった。
そんな中でも、特に面白いな、優れているな、と思ったのが米国大統領選のグラフィックインフォだ。
というか、単純にこのサイトの各所で使われれいるイラストやフォント、色感が個人的にとても好きだ。
大統領選を追っている人には、ぜひ訪れて欲しいサイトだ。

www.countyourself.in

このサイトのコンセプトは名前からも分かる通り、自分の州での支持率がパッと見で分かる工夫が散りばめられている。
私が気づいたのはこれくらい。
* 州のアイコンの色やグラフの配色を党で決めていて、共和党優勢か民主党優勢かで具合を変えている。(赤い州青い州ってやつ)
* 州のアイコンが州の形(文字だけより分かりやすいと思う)
* 候補者の顔イラストと、ステータスバーで双方の支持率の具合がパっと見で分かる
* 世論調査を円グラフと着色で見えやすく
* 数字の表示を大きめに。強調している
* アクセス元の地域の情報をランディングページとして表示している(日本はnon-usだよ)

f:id:masayannuu:20161020002213p:plain

ものの見せ方の良い勉強になった。
デザインやアプローチの仕方を変えてみると、小難しい話でもとっつきやすかったり、参加意欲を変えることができたりするものだろうなあ。

参考兼チェック用として、メモ。

【今日のひとこと】
やればできる
https://i.guggy.com/g7s3hfAlz6.gif

個人間の決済サービス「AnyPay」がいい感じ

かなりフレキシブルに使える個人間決済のサービスだなあという感想。

anypay.jp

何ができるの?
* 初期費用・月額費用なしで、個人間でクレジットカードを使った決済ができる
* 販売者は物販やデータ販売、サービスまで扱えて、簡単に販売ページを作成できる
* 共有はQRコードかURLを渡せばOK

f:id:masayannuu:20161017232953p:plain

特徴的だなあと思ったところ
* 月額固定費用の販売(いわゆるサブスクリプション)形式もできる
* 手数料が安い(月間5000万円以上の売上から2.8%) ※2016年10月現在
* 登録が簡単で、決済にクレカが使える(手軽にこれができるのは嬉しい)

導入コストが劇的に低いので、友人間の貸し借りや割り勘にも気軽に使える。
f:id:masayannuu:20161017234943p:plain サブスクリプションも可能なため、個人レッスン等にも相性がよい。
普通のオンライサロンよりも手数料も低いので、既存のユーザーを抱えている人であればAnypayを使って運営する人も出てくると予想。
ちなみに、売上の引き出しは200円の手数料のみ。

また、BASEとAnypayを比較すると、anypayの方がよりスコープを広い印象を持つ。
BASEが個人クリエイター向けのオンラインショップ作成コストを引き下げたが、Anypayは個人間の送金コストを引き下げた、という感じだろうか。

▼ダッシュボードはこんな感じ f:id:masayannuu:20161018000022p:plain
登録は簡単だった。
これは今度使うことがあるだろう。

飲み会の費用徴収とかも「アプリでお願いね〜」みたいなのがあたりまえになったりして。
f:id:masayannuu:20161018000600p:plain
マネーロンダリングとかグレーなことにも使われそう。

【今日のひとこと】
WEBがC2Cビジネスに影響ってほんとすごいな
https://i.guggy.com/ZDgVU4bhhz.gif