純喫茶 月畔

「月畔って何て読むの?」「月畔って読むのよ。」

第9回シンデレラガール総選挙の出口調査をやってみた話

4月の中頃からおよそひと月にわたって第9回シンデレラガール総選挙なるイベントが行われていた。これはいわゆる人気投票企画の一種で、投票内容をゲームクライアントからTwitterに投稿することができる。この投票の動向をリアルタイムで眺められたらめちゃめちゃ楽しそうだなと思ったので、出口調査botを作ることにした。

完全に自分がリアルタイムで票が動く様子を眺めるためだけに作ったものだったが、結果的に30万ツイートぶんのデータを集めることができた。

つかったもの

  • Python + Tweepy

    Twitterのデータをリアルタイムに取得するにはStreaming APIを利用するのが一番目的に適っていると考えた。なかでもTweepyのStreamingのモジュールは、かつてUserStreamが健在だったころに自分のTwitterにおける"ふぁぼ"*1を自動で取得していたことがあり馴染みがあった。

  • Google App Script(GAS)

    取得したデータはなるべくリアルタイムに表示したいと考えていた。そのほうが見ていて楽しいので。

    データを投げたらグラフとかまで描いてくれるダッシュボードサービスないかなーと思って探していたが、結局これまた使い慣れたGASを使うことにした。

    GASを使ってGoogleスプレッドシートを更新するとブラウザから見てもリアルタイムに更新されて見えるし、そのままグラフを作ったりできるので、思っていたことは達成されそうな気がした。GASは自前のサーバなしで簡易的なWebAPIのような仕組みを作ることができて重宝している。*2

実装

  • GAS

    スプレッドシート上でアイドルごとのシートを作成して、ツイートがあればその時刻と投票が行われた場所*3を対応するシートの末尾に追記していくことにした。

    GASでは指定されたURLに対するリクエストをトリガにして実行する機能があるので、今回はツイートを検知したら内容をJSONにしてここにPOSTすることにした。

    function doPost(e) {
        try{
            var PostData = JSON.parse(e.postData.contents);
            var ss       = SpreadsheetApp.getActiveSpreadsheet();
            var sheet    = getidolsheet(PostData.idol); // アイドル名のシートが存在すればそれを返し、なければ作成
            var date = new Date();
            console.log(PostData.idol);
                
            // 行の最後に追記
            sheet.appendRow([Utilities.formatDate(date, "Asia/Tokyo", "MM/dd HH:mm:ss"), PostData.moba, PostData.sta]);
            
            // 書き込んだシートを2番目に移動する
            ss.setActiveSheet(sheet);
            ss.moveActiveSheet(2);
        } catch(e){
            console.error('Error: ' + e);
        }
    }
  • Tweepy

    投票のツイートは、アプリなどからのツイート機能におけるデフォルトの文言の一部と完全に一致するものを抽出した。viaを指定することも考えたが、どうやらアプリ側の仕様として公式クライアントなどの他のアプリを通じてツイートするようになっているようで、viaでは判断ができないことがわかった。

    投票先の判定は、内部でアイドルの一覧を持っておいてその中で完全一致するものを探す方法を取った。投票ツイートは編集可能なため表記揺れが生じると都合が悪かったからである。

    見てわかるように、文言だけ真似て捏造したようなツイートまで素直に集計してしまうシステムになっているが、そのような不正なツイートが混ざる数と比べるとこれをちゃんと区別するというのはかなりコストが高すぎるように思った。

    ツイートを収集するコードは砂場あそびをするために借りていたGCEのインスタンス上で動かした。

失敗したこと

  • 初動が遅れた

    投票が始まってからの思い付きで作りはじめたので、開始直後の3時間くらいのデータを取れなかった。投票のツイートが一番盛り上がる時間帯のひとつだっただけにもったいなかったと思う。

  • スプレッドシートのセル数の上限にひっかかった

    稼動してから数日後に記録が止まっていたので、よくログを見てみたらスプレッドシートのセル数の上限である500万セルに達してしまったようだった。1ツイートにつき1行の3セルを使うようにしていたのだが、デフォルトの設定では利用しない列がD-Z列まで空白セルとして存在していたため不必要にセル数が膨らんでしまったようだった。使わない4列以降を削除することで対応した。

  • ツイートしたユーザの情報も記録しておくとよかった

    投票期間の終了が近いころに他のサイトで同様のデータ収集が行われているのを見たときに、ユーザIDを比較することで正味何人のユーザがツイートしているか比較しているサイトがあった。今回は日時と投票先しか記録していなかったが、たしかに1ユーザあたりのツイート数がアイドルによってどれだけ変わるか比較できたら楽しそうだったなと思った。ほかにも「このアイドルに投票した人はこのアイドルにも投票しています」みたいなのもやりたかった。

感想

やはり、投票の様子がリアルタイムで可視化されるのは面白く、毎日のように開いてはにこにこしながら眺めていた。特に投票期間の序盤は目紛しく順位が入れかわっていたため、かなり見応えがあった。

全体から取得したデータを見ていると、自分がTwitterで観測している人々の投票行動とはまた違った様子が現われていたのは面白かった。

投票後のツイートの裏にはひとりあたり数百から数万の票が隠れているものの、30万票ぶん集めたらそこそこの精度で実際の得票率を見積れるだろうと高をくくっていたが実際の結果を見ると自分の集めたデータと大きくずれていて驚いた。

自分の担当*4が10位付近にいることはなんとなく知っていたが、Twitterなどを見ていると「結果発表のとき10位にいるのを見てびっくりした!*5」みたいな感想があり、純粋な気持ちで発表を驚けなかったのはちょっと惜しかったかなというような気もした。

完全に自分が見て楽しむためだけに集めていたデータであるが、思いがけずそこそこの規模のデータになったので、今年の結果を分析してみて来年の結果を予想するのに用いることができたら面白いと思う。*6

*1:"いいね"ともいう

*2:たとえば他にもSlackのスラッシュコマンドをここで運用していたりする

*3:モバマスと呼ばれるブラウザゲームとスターライトステージと呼ばれるアプリの2種類のゲームからそれぞれ投票できた

*4:速水奏さん

*5:今年は中間発表などはなく、いきなり最終結果が発表される方式だった

*6:もっとも、毎年システムが変わるのでほとんどあてにできないような気もしている