HerokuとMySQLを利用したTwitterBotを作成した
製作動機
製作内容(初は初めて利用したことを表す)
- Rubyだとライブラリであるgemも豊富だろうし簡単に作れるかなと思った。実際その予測は当たっていたと思う。gemもそうだし先人の参考文献も多く、ずっと製作が一つの問題で停滞するようなことがなかった。
- Rubyのバージョンは製作の途中まで1.9.3を使っていたが結局このバージョンを使うことはなかった。既に借りている格安サーバーはRubyが1.8.5と古く、後述するハッシュの簡潔な表記(下記まめ知識1.)が通用せず、表記を直すのが手間だったのでやめた。
最終的にコードを実行するマシン(後述するHeroku上のマシン)は2.0.0(初)指定だったのでそれになった。切り替えに際して何も不都合は起きなかったのでよかった。 - 不都合が起きなかったのはもちろんRubyのgemのバージョン指定を行うGemfile(初)のお蔭による。このファイルはbundler(初)というGemが入力とするファイルで、bundle install(bundleだけでも同様)とコマンドを入力すると現在のディレクトリ(フォルダ)に存在するGemfileに従ってgemをインストールしてくれる。
インストール先を現在のディレクトリ以下(慣例ではvendor/bundle/)にするのが通の使い方らしいが、atomic(初)とかいうgemのインストールでコケるので見送った。最終的にHerokuにデータを上げるときも特に問題が起きなかったのでよかった。
コードの大きな流れは次のようになっている。
- optparse(初)というgemによりコマンドライン引数を解析する。
C++ではboostのoptionsを使ったと以前の記事に書いたが、同じようなものがRubyにも存在する。このように別の言語で馴染んだ概念が今から勉強する言語の概念にも存在すると、もう空気のようにその存在を意識しなくなる、と思う。注意という認知資源を使用しなくてもほぼ流れ作業で同じ機能を作れるということ。 - open-uriとnokogiriという2つのgemによりニコニコ動画から求めるデータを取得する。普段はCSSという部分指定可能な記法で取得していたが、今回はさらに複雑な指定ができるXPath(初)を使った(というか案外指定が難しく、使わざるをえなかった)。
サーバーに負荷がかからないように1秒1リクエスト以下に設定した。このリクエスト数の桁が数桁上がるとサイバー攻撃になってしまうと認識しているのでここには注意した。岡崎librahack事件では1秒1リクエスト以下でもシステム側の不都合により図書館側のサーバーマシンが内部エラーを出してしまい大変な問題となったが、ニコニコ動画でそのようなことはありえないと思われたのでこの頻度にした。 - ActiveRecord(ほぼ初)というgemによりMySQL(初)というデータベースに2.の取得データを保存する。これは後で呟く際に重複検知のために使う。
- Rubyのコードから呟くために必要なデータをtwitterというgemによりセットする。
(Twitterのbotに必要なのはコンシューマnとアクセスn (n = トークン, シークレット)の4つだが、これはTwitter社のDeveloperページに(呟きたいアカウントで)ログインして手順を踏むと取得できる。アクセスnについては初期設定のReadをRead and Writeに変える必要があるなど、比べるならばダウンロードしたシェルスクリプトtekitou.shを実行する前にchmod +x tekitou.shで実行権限を付与する必要があることに似ているなと感じた。おそらくは製作上の安全策だと思う。) - 重複かどうかを3.の成否によって判定し、重複でなければ呟く。
- 成功した旨を表すメッセージを(自己満足のために)表示して終了。
- それぞれ細かい内容は次のようになっている。
- オプションとして渡すように指定したものは以下。
--yaml 3.で使うデータベースを指定するための.ymlファイル(初)。
これにより、データの保存先を柔軟に変更できる。
--database 3.で使うデータベースを詳細に指定する。(ymlファイルの中にはさらに複数のデータベースが存在しているので、どのデータベースを使うかを名前で指定する。いま考えてみるとymlファイルは一つにまとめられたかもしれない。設計ミスったな)
--tweet gamefes3
@gamefes3のアカウントで呟く。なお--tweetを付けないと、呟きもせずデータベースにも保存せず、単に画面に取得したデータを表示するだけになる。これはきちんと2.で取得されているかを確かめる際に非常に役立つ。
このようなオプション(選択肢)が1つあるかどうかで開発の効率が劇的に変わるようだ。上記のデータベース指定もそうだが、仮にこれらをコードの中に直接書くハードコーディングをしていたとしたらどれだけ無駄な手間がかかったか分からない。
--creator
動画投稿者の名前も含める。これが実はおそらくほかのBotには一切存在しない機能。なぜなら投稿者名は動画を表示しないと取得できないので(検索結果やランキングには載っていない)、複数のページにわたる情報を統合する必要があるからだ。
これをデフォルトでONにしなかった理由は、なぜかローカルではZbufErrorが起きるからだ。この原因が特定できなかったので今はデフォルトから外している。
--query
検索するタグの文字列を変更する。デフォルトは「ニコニコ自作ゲームフェス3」に指定されている。
--sort
検索タイプを指定する。デフォルトは「投稿が新しい順」になっている。
--withdatabase
ツイートはしない、しかしデータベースには保存をしたいときに使う無理やりなオプション。
実際に動かす
プログラム自体は以上だけで動くのだが、実際にはこれに加えてRubyのそのプログラムを半永久的に無料で動かしてくれるマシンというものが必要だ。そんな都合のよいものがあるのかと言われそうだが、現代には恐ろしいことに、大量に存在する。Herokuというサービスが、ストレージ300MB、メモリ512MB、MySQL5MB、ライブラリはgemならほぼ自由、というサーバ環境を無料で貸してくれる。サイトの人気が出てきてそんなショボいスペックで足りなくなったら、お金を払えばすぐにでもメモリもデータベースも何百倍にも拡大できるよ、というビジネスモデルらしい。フリーミアムの一環だろう。
Herokuにデータを上げて動かす(deployデプロイと言う)際には以下の事項を学んだ。
- Herokuではまずユーザーアカウントを登録した後、コマンドラインから各種操作を行えるツール(Heroku Toolbelt)をインストールする。このツールというのが曲者で、言ってみればHerokuに対して行える操作は基本的にこのツールに用意されたもの(とブラウザからの操作)を超えない。僕が借りているレンタルサーバのように、ftpという形式でアクセスして好き勝手中を眺めまわせる、という風にはいけないのだ。これは有料でもそうらしく、やれることがいちいち制限されていて非常に狭苦しい感覚を受けた。
追記(13/12/28): heroku run lsなどでさまざまに調べられるらしいが、heroku runで呼び出されたものはOne-Off dynoと呼ばれるものらしく、これもDynoの月利用時間に含まれる。すなわち1つのdynoを最大で月744時間動かしている僕の環境の場合は、無料で使いたいならばOne-off dynoの利用は6時間以内に収める必要がある。これはなかなか難しいな。どうしよう。
また、heroku run lsと同じことはheroku run bashのあとにlsでもできる。こちらのほうがよく使うだろう。ただし公式ドキュメントによるとdynoごとにデータは別々(プロジェクト全体をコピーしているのか?)なのでrmなどをしてもデータは変更されないらしい。この点はちょっと直観に反するので注意する。
- gitというデータ管理・送信ツール(これはHerokuとは全く独立に開発されたツールである)によってデプロイすると、自動的にGemfileを読み込んで必要なgemをHeroku側にインストールしてくれる。これは非常にありがたかった。その代り、デプロイするたびにチェックが入るために1回のデプロイに20秒程度かかる。これもまたftpの高速さに慣れていると面食らう部分である。
- heroku logsなどを使うと、普通のレンタルサーバー同様にサーバーで起きているエラーなどが見れる。しかしこれも若干見にくい。
- また、Heroku上ではローカル(手元でコードを作ったマシンのこと)では起こらなかったバグがいくつか生じている。
たとえばログが1,2,3,4,5と表示されるところで3,4,1,2,5などと表示される。おそらく無料プランだからさまざまな計算順序の保証がないということなのだろうが、使い始めて1日目で気付くほどだとは思わなかった。 - なお、Herokuでは1dyno(1つのプロセスぐらいの意味だと思う)しか無料でできない(正確には、全てのdynoの合計稼働時間が750時間/月を超えない間は無料)なので、今回はClockwork(初)というgemにより定期実行をさせている。これもまた面白いGemで、ただ単に1秒カウントしているだけのプログラムの間に各種のプログラムの実行を挟むことで、実用的なものにまで発展させたものと言える。非常にお世話になった。このgemがなければbotを動かせなかったとすら思う。
まめ知識
- Rubyのハッシュ表記ってv1.9ぐらいまでは:hoge => '値'しか許されてなかった時代があったのね。hoge: '値'のほうが簡潔なのになぜこう書くのだろうと思ってたけど後方互換だったのか。
- 自宅サーバーはルータの関係でできなかった。いろいろと探したけど、結局実用的に使えたのはCron程度のものだった。
全体の感想
最初はまさか実際に使えるようなレベルに達するとは思っていなかった。50行程度の小さいものを作ろうとしていたのに、.ymlやMySQL、Herokuや各種Gemなどを組み合わせることによってまだまだ可能性が開けていくと作っている最中に発想が膨らんでいくので、ほとんど手が止まらなかった。
また、データベースサーバと言われても以前はあまり実感がわかなかったが、実際にHerokuのDBサーバにローカルからアクセスすることで
マシンを特定するためのホスト名と
権限を指定するためのユーザー名と
本人確認のためのパスワードと
そのユーザーが使えるデータベースの名前
の4つでネットを介して接続できるデータの集合体の管理プログラム、という実感が非常に沸いた。Heroku自体がPaaSという、使ったことがない人にはなんじゃそりゃと思われるような存在なので、これについてもイメージが湧いたのは良かった。
実際、今回の検索結果表示はまだまだいろいろな拡張の可能性がある。以下に例を挙げる。
拡張の例
- 現状では検索結果の最初のページだけを検索対象にしているが、これを2,3,4,5ページ目に拡張するのは非常に容易なことである。たとえばその結果を500件分取得して、1ページで閲覧できるようなHtmlに書き出せば、埋もれてしまった動画を素早く取り出せるWebページが誕生する。
- 現状では1つのタグに1つのTwitterアカウントを対応させているが、これを複数のタグと複数のアカウントの入り混じった状態にすることも容易である。たとえば、
アカウントA = もっと評価されるべき、才能の無駄遣い
アカウントB = もっと評価されるべき、もっと評価されるべきMAD、もっと評価されるべきMMD
のように、重複させることも容易である。
- 現状ではMySQLの容量が5MB制限なので、数か月のログを取ることは難しく途中で廃棄を挟まなければいけないが、MongoDBというデータベースであれば500MBまで無料で使えるので数年ぐらいその作業が不要になる。「ちっちゃなビックデータ」解析になるのではないだろうか。ただし、MongoDB(初)への移植は多少手間がかかるだろう。
総じて、新しい技術に多く触れられたことでよい冬休み1週間目になったと思う。