Node.js、Express、sentiment、ntwitter を使用して感情分析アプリケーションを作成する

2014年 7月 02日
PDF (689 KB)
 

Build a Twitter sentiment analysis application using Node.js modules

03:54  |  Transcript
author image - scott rich

Scott Rich

IBM Distinguished Engineer, Cloud Architect, Rational Jazz founder

@ScottRich

IBM Bluemix™ にサインアップ
無償のサービス、ランタイム、インフラを含むクラウド・プラットフォームが、新たなモバイルやウェブ・アプリのクイックな構築とデプロイを実現します。

私はソフトウェア・アプリケーション開発者として、次の問題を解決しようとしてきました。その問題とは、ユーザー、特にオンライン・ユーザーから即座にフィードバックを得るにはどうしたらよいかというものです。例えば、アプリ、製品の発売、キャンペーン、時事問題などに対する Twitter の世界での反応を瞬時に読み取れると、非常に役立ちます。反応は肯定的でしょうか、それとも否定的、あるいは中立的でしょうか?

私は、アプリケーションで Twitter 上のツイートを基に人々の感情を短時間で正しく判断するための要件についてじっくり考えました。その結果、私がそのアプリケーションに望むことは、短時間で開発でき、Twitter Web サービスとインターフェースを取り、単純なモバイル・インターフェースを持ち、時間の経過と共に増えていく大量のデータを素早く分析できるものであることです。

既存のビルディング・ブロックを使用すると、面白い、ちょっとした感情分析アプリケーションを非常に短時間で作成することができます。

私は、このアプリケーションを PaaS アプリとして作成することにしました。使用するものは、JavaScript と、PaaS アプリによく使用される Node.js ランタイムです。このアプリは、Cloud FoundryHeroku などの PaaS 環境でサポートされているサービス・コンポジション・プログラミング・モデルを使用します。私は、このアプリを JazzHub で開発することにしました。こうすることで、アプリケーションを公開する場が得られるとともに、ブラウザー・ベースの便利な IDE を使用してコードを作成することができます。

同様のアプリを作成するために必要となるもの

 
  1. Node.js と Node.js 開発環境の基本を理解していること。
  2. Node.js のモジュールが 3 つ必要です。

見る:「Node.js 入門」(動画によるデモ)

以下のステップは最も基本的な方法を説明したものです。もっと高度なツールを使用して開発を迅速に行うには、「他のツールについての検討」を参照してください。

ステップ 1. Express アプリケーションを作成する

 
  1. 以下のように node コマンドと npm コマンドを実行します。
    図 1. システム上に Node.js と NPM がセットアップされていることを確認する
    node コマンドと npm コマンドを実行した場合のスクリーン・ショット
  2. できるだけ単純な Node.js Express アプリとして、Hello Node.js を作成します (リスト 1 を参照)。

    新しいディレクトリーの中に、Node.js アプリのメイン・ファイルである app.js ファイルを作成します。このファイルではまず、Express モジュールが必要であることを Node.js に対して宣言してから、Express アプリを作成します。そして /hello に対するリクエストがリスト 1 の匿名関数で処理されるように、処理ルートを登録します。/hello のハンドラーは Express のヘルパーを使用して単純なテキスト・レスポンスを送信します。send メソッドはコンテンツ・タイプや長さのデフォルト設定などの詳細を処理します。Express を使用すると、ベースで行われる HTTP によるやりとりの詳細の一部を無視することができます。

    リスト 1. Hello Node.js
    var port = (process.env.VCAP_APP_PORT || 3000);
    var express = require("express");
    
    var app = express();
    
    app.get('/hello', function(req, res) {
        res.send("Hello world.");
    });
    
    app.listen(port);
    console.log("Server listening on port " + port);

    注: このプログラムをもっと簡単なものにすることもできますが、数行のコードを追加することで、このプログラムを CloudFoundry ランタイムで実行できるようになります。CloudFoundry の中では、アプリのポートは別のポートに再配置されます (再配置先のポートは CloudFoundry 環境で見つけることができます)。

  3. このアプリに関する追加情報を Node.js に提供します (リスト 2)。

    package.json ファイルを作成することで、Express モジュールを使用することをアプリに対して宣言し、このアプリに「SimpleSentimentAnalysisApp」という名前を付けます。このアプリは NPM レジストリーに公開されるように意図されたものではないので、プライベートとしてマークアップすることができます。最後に、このアプリが Express モジュールのバージョン 3.x に依存することを宣言します。

    リスト 2. package.json
    package.json for Hello Node
    {
      "name": "SimpleSentimentAnalysisApp",
      "description": "Simple Sentiment Analysis App",
      "version": "0.9.0",
      "private": true,
      "dependencies": {
        "express": "3.x"
      }
    }

Hello Node.js アプリを実行してテストする

 

次に、以下のステップで Hello Node.js アプリを実行してテストします。

  1. アプリを実行する前に、このアプリに対して宣言されている依存関係 (さらにそれらの依存関係、そしてまたさらにそれらの依存関係、等々) を取得するために NPM を使用してノード環境を準備します。
    図 2. ノード環境を準備する
    npm install コマンドを実行した場合のスクリーン・ショット

    NPM のインストールが成功すると、node_modules ディレクトリーが作成され、必要なモジュールがそのディレクトリーに配置されます。

  2. コマンドラインで「node app.js」と入力します。
    図 3. node app.js コマンドを実行する
    node app.js コマンドを実行した場合のスクリーン・ショット
    「Server listening on port 3000 (サーバーはポート 3000 をリッスンしています)」というメッセージによって、アプリが起動されてリクエストをリッスンしていることが確認されます。
  3. ブラウザーで http://localhost:3000/hello にアクセスし、このアプリの動作をテストします。
    図 4. アプリの動作をテストする
    ブラウザーで http://localhost:3000/hello にアクセスした場合のスクリーン・ショット

ステップ 2. アプリケーションに感情分析を追加する

 

基本的なアプリが実行されるようになり、環境を設定できたので、今度は Andrew Sliwinsky 氏による非常に優れた sentiment モジュールを使用して実際の機能を追加します。彼は sentiment モジュールのことを「AFINN-111 単語リストを使用して入力テキストの任意のブロックに感情分析を行う Node.js のモジュール」と表現しています。これから説明するように、このモジュールは感情分析を比較的単純に実装したものであり、肯定的感情、または否定的感情のどちらかを表すと評価された一連の英単語からなる辞書を使用して、テキスト内の単語を単純にスコアにします。

  1. sentiment モジュールを使用するには、このモジュールが依存関係として含まれるように package.json を更新します。
    リスト 3. package.json を更新する
    {
      "name": "SimpleSentimentAnalysisApp",
      "description": "Simple Sentiment Analysis App",
      "version": "0.9.0",
      "private": true,
      "dependencies": {
        "express": "3.x",
        "sentiment": "0.2.1"
      }
    }
  2. npm update コマンドにより、この新しいモジュールを取得するように Node.js に指示します。(注: ドキュメントでは、npm install コマンドでも新しい依存関係が取得されるはずであることが示されていますが、私の場合はそうなりませんでした。)
  3. この新しい sentiment モジュールを読み込みます。
    var express = require("express");
  4. sentiment モジュールを試すための別の処理ルートを作成します。
    リスト 4. 感情分析アプリのインターフェースのコード
    app.get('/testSentiment',
        function (req, res) {
            var response = "<HEAD>" +
              "<title>Twitter Sentiment Analysis</title>\n" +
              "</HEAD>\n" +
              "<BODY>\n" +
              "<P>\n" +
              "Welcome to the Twitter Sentiment Analysis app.  " +   
              "What phrase would you like to analzye?\n" +                
              "</P>\n" +
              "<FORM action=\"/testSentiment\" method=\"get\">\n" +
              "<P>\n" +
              "Enter a phrase to evaluate: <INPUT type=\"text\" name=\"phrase\"><BR>\n" +
              "<INPUT type=\"submit\" value=\"Send\">\n" +
              "</P>\n" +
              "</FORM>\n" +
              "</BODY>";
            var phrase = req.query.phrase;
            if (!phrase) {
                res.send(response);
            } else {
                sentiment(phrase, function (err, result) {
                    response = 'sentiment(' + phrase + ') === ' + result.score;
                    res.send(response);
                });
            }
        });

    Web アーキテクトや Web デザイナーには申し訳ないことに、この新しい関数には、力ずくによる単純さが至るところに見受けられますが、後日、もっとインテリジェントな方法で記述することを私は約束します。今はとりあえず、フレーズを入力すると、sentiment による評点が返されるフォームを作成します。関数呼び出しで重要なのは sentiment(phrase, function) です。Node.js の精神に従い、この sentiment ライブラリーは非同期であるため、感情分析の結果はコールバック関数で処理されます。

  5. アプリを再起動し、ブラウザーで http://localhost:3000/testSentiment にアクセスします。
    図 5. 感情分析アプリのユーザー・インターフェース
    ブラウザーで http://localhost:3000/testSentiment にアクセスした場合のスクリーン・キャプチャー

ご覧の通り、感情分析のための素晴らしいユーザー・インターフェースができました!

感情のサンプルにスコアを付ける

 

以下の表は、この感情分析アプリを仮に実行した場合の結果を示しています。

表 1. 仮に「Node.js」について分析した場合の結果
感情スコア解釈
Node.js is cool, I love it4非常に肯定的な感情
Node.js is uncool, I hate it-3非常に否定的な感情
Mi piace Node.js. Node.js é bellissima0何も感情は検出されません。sentiment モジュールには英語の辞書しかないからです。
Node.js is not cool, I do not love it4おっと!最初の例と同じく非常に肯定的と解釈されてしまいました。sentiment モジュールは文法や否定形を認識しないからです。

この表から、この方法の限界が見えたはずです。つまり、sentiment モジュールには英語の辞書しかないことに加え、このモジュールは文法や否定形を認識しません。それでも、大部分のツイートの要点を把握するにはおそらく十分と言えるでしょう。

ステップ 3. アプリケーションを Twitter に接続する

 

Twitter の Stream インターフェースを使用すると、ある時間のあいだに特定のトピックに関して Twitter に投稿されたすべてのコンテンツにアクセスできるため、このインターフェースは感情の傾向を観察するのに最適です。

このアプリは公開された Twitter ストリームに対してのみ動作するので、Twitter の Application-only Authentication という認証方法で十分です。この方法は OAuth による交換を単純化しており、ユーザー・ログインを促す表示や、ユーザーの同意を促すための表示が必要ありません。この方法を使用するには、アプリ固有の OAuth 鍵のセットが必要であり、それらの鍵はエンコードされてアプリに含まれています。詳しくは、Twitter のストリーミング API についての資料を参照してください。

このアプリの重要な要素としては Twitter への接続もあります。Twitter に接続することで、アプリはツイートのストリームを取得して分析することができます。

  1. Twitter API への Node.js インターフェースとなる ntwitter モジュールを使用して、1 つ以上のキーワードを含む公開ツイートのストリームを要求します。
  2. 皆さんの Twitter 開発者アカウントの下に鍵セットを登録することにより、それらの鍵セットを取得します。アプリを作成、管理するための Twitter の「My Applications」ページにアクセスします。
  3. いくつかの関数をアプリに追加し、Twitter への接続をテストします。まず、ntwitter と OAuth 鍵を使用して twitter オブジェクトを作成した後、これらのクレデンシャルを検証するための単純な呼び出しを行います。

    リスト 5. Twitter への接続をテストする
    // Sample keys for demo and article - you must get your own keys 
    //   if you clone this application!
    // Create your own app at: https://dev.twitter.com/apps
    var tweeter = new twitter({
        consumer_key: 'your',
        consumer_secret: 'keys',
        access_token_key: ‘go',
        access_token_secret: 'here'
    });
    
    app.get('/twitterCheck', function (req, res) {
        tweeter.verifyCredentials(function (error, data) {
            res.send("Hello, " + data.name + ".  I am in your twitters.");
        });
    });
  4. アプリを再起動して /twitterCheck にアクセスし、Twitter への接続とログインが機能していることを確認します。
    図 6. Twitter への接続をテストする
    Twitter への接続をテストする

    このテストの結果、Twitter に接続しているアプリの所有者が私であると認識されていることに注意してください。

  5. Twitter への接続を確立できたので、Twitter で 1 つ以上のフレーズをモニターするための関数を 1 つ追加します。これを行うための非常に簡単な方法を提供するのが ntwitter です。
    1. stream() を呼び出し、モニター対象のフレーズを渡す。
    2. コールバック関数を使用して、ツイートが受信されるごとにそのツイートを処理する。

      Twitter Stream API を使用すると、アプリはストリームを開き、データがある限りデータを取得し続けることができます。この関数はサーバー上でストリームを開いたままにすることができるとともに、ストリームを処理しながらカウンターや平均的な感情を更新できるため、Node.js アプリに最適です。

    3. 単純なインターフェースを作成して、モニター対象のフレーズを設定するとともに、ツイートが受信されるのに合わせてモニター結果を表示します。もっと高度なアプリであれば、ツイートをデータベースに格納し、もっと複雑な処理ができるはずです。

リスト 6 はコードの最も重要な部分を示しており、ストリームを開き、受信したツイートのサンプルをログに記録しています。

リスト 6. ストリームを開いてツイートをログに記録する
app.get('/watchTwitter', function (req, res) {
    var stream;
    var testTweetCount = 0;
    var phrase = 'bieber';
    tweeter.verifyCredentials(function (error, data) {
        if (error) {
            res.send("Error connecting to Twitter: " + error);
        }
        stream = tweeter.stream('statuses/filter', {
            'track': phrase
        }, function (stream) {
            res.send("Monitoring Twitter for \'" + phrase 
                + "\'...  Logging Twitter traffic.");
            stream.on('data', function (data) {
                testTweetCount++;
                // Update the console every 50 analyzed tweets
                if (testTweetCount % 50 === 0) {
                    console.log("Tweet #" + testTweetCount + ":  " + data.text);
                }
            });
        });
    });
});

ステップ 4. 全体をまとめる

 

これで、アプリを完成させるために必要なパーツがすべて揃いました。それらのパーツを以下に記載します。

  • さまざまなページを提供するための基本的な Express アプリ
  • テキストに含まれる感情を評価するための感情分析関数
  • 分析対象であるツイートのソースを提供する、Twitter への接続

あと必要なことは、これらを組み合わせて興味深いことをすることだけです。完成したアプリは、1 つのフレーズを入力するようユーザーに促し、そのフレーズでフィルタリングされたストリームを開くために Twitter を呼び出し、そのフレーズに一致する各ツイートの感情を分析し、時間の経過に伴う感情の変化をモニターするページを作成します。

リスト 7 はアプリケーションの全体を示しています。ユーザー・インターフェースは非常に単純なままであり、すべて昔ながらの Struts スタイルで作成されています。

リスト 7. アプリケーションの全体
var tweetCount = 0;
var tweetTotalSentiment = 0;
var monitoringPhrase;

function resetMonitoring() {
    monitoringPhrase = "";
}

function beginMonitoring(phrase) {
    var stream;
    // cleanup if we're re-setting the monitoring
    if (monitoringPhrase) {
        resetMonitoring();
    }
    monitoringPhrase = phrase;
    tweetCount = 0;
    tweetTotalSentiment = 0;
    tweeter.verifyCredentials(function (error, data) {
        if (error) {
            return "Error connecting to Twitter: " + error;
        } else {
            stream = tweeter.stream('statuses/filter', {
                'track': monitoringPhrase
            }, function (stream) {
                console.log("Monitoring Twitter for " + monitoringPhrase);
                stream.on('data', function (data) {
                    // only evaluate the sentiment of English-language tweets
                    if (data.lang === 'en') {
                        sentiment(data.text, function (err, result) {
                            tweetCount++;
                            tweetTotalSentiment += result.score;
                        });
                    }
                });
            });
            return stream;
        }
    });
}

function sentimentImage() {
    var avg = tweetTotalSentiment / tweetCount;
    if (avg > 0.5) { // happy
        return "/images/excited.png";
    }
    if (avg < -0.5) { // angry
        return "/images/angry.png";
    }
    // neutral
    return "/images/content.png";
}

app.get('/',
    function (req, res) {
        var welcomeResponse = "<HEAD>" +
            "<title>Twitter Sentiment Analysis</title>\n" +
            "</HEAD>\n" +
            "<BODY>\n" +
            "<P>\n" +
            "Welcome to the Twitter Sentiment Analysis app.<br>\n" + 
            "What would you like to monitor?\n" +
            "</P>\n" +
            "<FORM action=\"/monitor\" method=\"get\">\n" +
            "<P>\n" +
            "<INPUT type=\"text\" name=\"phrase\"><br><br>\n" +
            "<INPUT type=\"submit\" value=\"Go\">\n" +
            "</P>\n" + "</FORM>\n" + "</BODY>";
        if (!monitoringPhrase) {
            res.send(welcomeResponse);
        } else {
            var monitoringResponse = "<HEAD>" +
                "<META http-equiv=\"refresh\" content=\"5; URL=http://" +
                req.headers.host +
                "/\">\n" +
                "<title>Twitter Sentiment Analysis</title>\n" +
                "</HEAD>\n" +
                "<BODY>\n" +
                "<P>\n" +
                "The Twittersphere is feeling<br>\n" +
                "<IMG align=\"middle\" src=\"" + sentimentImage() + "\"/><br>\n" +
                "about " + monitoringPhrase + ".<br><br>" +
                "Analyzed " + tweetCount + " tweets...<br>" +
                "</P>\n" +
                "<A href=\"/reset\">Monitor another phrase</A>\n" +
                "</BODY>";
            res.send(monitoringResponse);
        }
    });

リスト 7 に示すように、最後にアプリのルート・パスに少し内容を追加しています。最初から、このアプリは歓迎のプロンプトを表示し、モニター対象のフレーズを収集します。その後、モニター対象のストリームを設定し、その結果のページを表示します。sentiment 関数によってツイートのコンテンツを渡し、ツイートのカウントをインクリメントし、感情を記録するように、ツイーターのストリーム・コールバックを更新します。英語以外のツイートはフィルタリングで除去されます。

このアプリは表示のために sentimentImage() 関数を使用しています。感情の値を指定すると、この関数はその値に対する画像の URL を返します。幸せと不機嫌の範囲は任意です。ほとんどのトピックに関し、感情の範囲が驚くほど狭いことに私は気付きました。これはツイートの長さが比較的短いことによるのかもしれません。皆さんはこれらの範囲を自由にいじってみてください。

では、試してみましょう。いくつかのテストを実行した結果を以下に示します。

図 7. Twitter の世界では Justin Bieber に好感を持っています
幸せな表情のアイコンで「Justin Bieber」というトピックに関する感情が好意的であることを表しており、分析されたツイートの数は 24 個です。
図 8. Twitter の世界ではシリアに対する感情はどっちつかずです
はっきりした表情のないアイコンで、「シリア」というトピックに関する中立的な感情を表しており、分析されたツイートの数は 5 個です。
図 9. Twitter の世界では、NSA、Snowden、PRISM に関して否定的です
怒った顔のアイコンは、「NSA、Snowden、Manning、PRISM」というトピックに関する否定的な感情を表しており、分析されたツイートの数は 4 個です。

他のツールについての検討

 

ステップ 1 で、node コマンドと npm コマンドを使用してアプリケーションを管理、実行する方法を説明しました。すべてのコーディングは、テキスト・エディターで容易に行うことができます。ただし、私は実際には開発作業を容易にするために、他のツールを2、3 個使用してアプリを開発しました。

Project Icap の技術プレビューには、Node.js を使用した開発のための Eclipse プラグインが含まれており、これらのプラグインが JavaScript の基本的な構文の強調表示や、Node.js アプリケーションの起動支援を行っています。これらのプラグインは起動されたアプリケーションを自動的にリロードすることで、繰り返し処理を迅速に行う簡単な方法を提供します。

また、私は最終的に、Cloud Foundry に対応した Node.js ランタイム環境 (CloudFoundry.com や IBM の新しい Bluemix プラットフォームなど) にアプリをプッシュしたかったため、最終的なアプリには、そうした環境に必要なファイルがいくつか追加で含まれています。例えば、manifest.yml ファイルには、アプリとそのリソースからの Cloud Foundry ランタイムに対する要求が記述されており、npm-shrinkwrap.json ファイルは Cloud Foundry の Node.js ランタイムに対し、どのモジュールを使用してアプリをデプロイする必要があるかを正確に指示します。

まとめ

 

私は Node.js、Express、ntwitter、sentiment を使用してこのアプリを開発する中で、Twitter へのアクセスや感情分析などの機能が Node.js のモジュールとしてパッケージ化されていると、それらの機能がいかに利用しやすくなるかを実感しました。このことから、なぜ Node.js が Web アプリやモバイル・アプリの開発でよく使用されるのかを容易に理解することができます。

私は今、Express を使用して、もっとプロフェッショナルなユーザー・インターフェースをアプリに追加してみたいと思っています。このアプリを改善する余地は、まだまだ沢山あると思います。; )


関連トピック:Node.jsJavaScriptSocial analytics

コメントの追加

注意: HTML コードは、コメント内ではサポートされません。


残り 1000 文字

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Cloud computing, Mobile development
ArticleID=953036
ArticleTitle=Node.js、Express、sentiment、ntwitter を使用して感情分析アプリケーションを作成する
publish-date=07022014