オウムらしからぬ適当なことを喋る
目次 [Raspberry pi 3 + Python = Twitter Bot]
- Twitterの開発者登録からツイートをポストするまで
- Python-Twitterモジュールの情報まとめ
- メンションをもらったら返答するようにした
- 異常終了の原因を追及できるように例外の処理を追加
- マルコフ連鎖モジュールのPython3対応作業
前回までに、マルコフ連鎖に関連するモジュールをいくつか改造しまして、動作するようにしました。これは、ツイートを自動生成する処理の基板にあたるものです。今回はようやく独自のツイートを生成して投稿するところを作ろうと思います。
つくっていて急に心配になったのですが、オウムも場合によっては人語を喋りますよね。九官鳥じゃなかったかと心配になりました。他所様の動画ですが、これすごいですね。オウムが笑うと子供がつられて笑うので、ますますオウムが笑うようになるという…… 恐ろしい連鎖です。
ツイート生成の方針
これまでに、トレンドワードを取得してリストにするところまではできていました。これをツイートにまとめて投稿することもできます。
トレンドを取得すると、キーワードだけでなく、関連する情報もついてきます。キーワードは trend.name という名称で取り出せるのですが、他にも trend.url という名前で、関連ツイートを検索するURLが取得できます。このURLをSearchして、他のツイートをまとめ、それをマルコフ連鎖モジュールで「学習」したあと、自分なりのツイートを生成することにします。できあがりはこんな感じです。
ただ、取得したツイートを学習させるだけだと、他の方のツイートをそのままパクツイしたようなものも生成されてしまいます。それに他人を中傷するツイートも少なからずあるので、安易にマネしてしまうのは危ないこともあります。そんなわけで、カタカナに展開することでインパクトを和らげることにしました(まぁ、オウムですし……(逃避))。ただ、全部カタカナだと何を言っているのかわからないので、トレンドワードそのものは残しておくことにしています。
技術的なところ
マルコフ連鎖は、前回Python3対応の作業をした python-markovchains と、その前提モジュールとなる py-extractword を使用します。
具体的には、こんな流れになりますね。あとはひとつひとつ地道に作っていくだけです。
今回のハマリポイント
markovchains.make_sentence()が、無限に長い文章を生成して応答を返さなくなることがあります。プログラムがCPUを100%使用する状態のまま止まってしまい原因を探るのに苦労しました。そういうときはpython -m pdbを使用してデバッガから動作を確認するのが早道かとおもいます。
この問題は、python-markovchainsのmake_sentence()関数の中に処理を追加して対処しました。ツイッターではどうせ140文字を超えたらツイートできないのですから、漢字を含めて100文字くらいで終わってくれないと、使えません。ある程度長くなったところで諦めて結果を返すように動作変更しました。GitHUBからforkしたのは今回が初めてですが、やってみるものですね。
次回予告?
気の向くままに作業していますが、それでもフォローしてくれる人がいるので、フォローが届いたらフォロー返しをしようかと思います。多くのBotはこちらから積極的にフォローしていきますが、そこまでやっても得るものがあるかどうか…… しばらくはやめておきましょう。
Raspberry Pi 3 Model B (Element14)
- 出版社/メーカー: RASPBERRY-PI
- メディア: エレクトロニクス
- この商品を含むブログ (1件) を見る
図解入門 最新人工知能がよ~くわかる本 (How-nual図解入門Visual Guide Book)
- 作者: 神崎洋治
- 出版社/メーカー: 秀和システム
- 発売日: 2016/07/05
- メディア: 単行本
- この商品を含むブログを見る
Raspberry pi 3 + Twitter [RaspiParrot] 他のプログラムのPython3対応作業
目次
- Raspberry pi 3 + Python = Twitter Botの起動(とりあえず投稿するところまで)
- Python Twitterのメモ
- Raspberry pi 3 + Python = Twitter Bot(Mentionをとりあえず返せるところまで)
- 例外の処理を追加
現在の作業目標は、Twitterのトレンドワードに関連したツイートを多数集めて、それらに使われている単語を合成し、何か面白そうなツイートができないかな、というものです。
トレンドワードに関連するツイートを収集
Twitterにはトレンドという情報があります。多くの人がつぶやいているキーワード(ニュースとかテレビ番組とかの話題が多い)がリストになったものです。この情報の入手は簡単で、Twitter APIとしてすでに実装されていますので、割合スムーズに収集することができました。部分的なソースを引用しますが、GetTrendsWoeid()という関数が、Twitter APIの下のマニュアルの機能を呼び出しています。WOEIDというのは、地球上の特定の場所を示す郵便番号的なもののようです。私のソースでは23424856を使っており、これは日本を示すWOEIDです。都道府県単位でも変更できるようです。
def CheckTrendWord(api): ''' 時々トレンドを入手してコメントしたりする ''' global trendticker try: logger.debug(u"trendticker:%s" % ( trendticker )) if trendticker != 0: trendticker -= 1 else: trendticker = int(inifile.get("trend","frequency")) # woeid = 23424856は、日本のトレンドワード trend = api.GetTrendsWoeid(woeid=23424856) # トレンドに関連したツイートを調査 AnalyzeTrendTweet(api, trend) # トレンドワード一覧をツイート PostTrendWord(api, trend) except TwitterError as e: logger.warning(u"CheckTrendWord Error:%s" %( e.message ))
で、とりあえず @RaspiParrot はトレンドワードを並べて定期的にツイートする機能を実装しています。Botにありがちな機能ですね。昨日はポケモンGO関係のキーワードばかりになっていてテストとしてはイマイチな感じでした。
しかし、正直これだけだと、フォローしたいアカウントとは言えないですね。もっと面白くしなければ……
独自のツイートをつぶやかせたい
マルコフ連鎖、という技術があります。たくさんの既存の文章から、単語と単語のつながりに関する情報を学習して、ある単語の後には別のある単語が来ることがある…… といった情報を集積することで、今度はその情報をもとになんとなく意味が通りそうな文章を生成できるというものです。単語と単語のつながりしか見ていないので、文章や文脈の全体としては、まとまった内容になりません。何も知らずに読んでいると、酔っているか、頭のおかしい人が支離滅裂なことを言っているように見えます。でも、今作っているのはBotですし、名前もオウム(Parrot)ですので、その辺りはご愛嬌ということにしまして、ともかく独自の文章を生成するプログラムの作成を進めることにします。
実は何年も前に書いたマルコフ連鎖のプログラムが手元にあるのですが、Perlで書かれているので、Pythonに書き換えるのも手間がかかります。どうしたものかと思っていたらすでに作成している方がいたので、GitHubにあったソースコードをforkさせてもらうことにしました。
すぐ使えるかと思ったのですが、Python 2系列で使うことを目的にしていて、そのままでは動作しませんでした。また、MySQLやPostgreSQLのPythonモジュールがインストールされていないと、これらのデータベースを使わない場合でも動かない、というちょっと困った依存についても排除したかった(だってそれらをインストールするにはまたPython3の壁に突き当たるので……)ので、forkして修正させてもらいました。その私のレポジトリがこちら。
まだ広汎なテストはできていませんが、少なくともサンプルレベルであれば動きます。前提パッケージになっているpy-extractwordの方もPython3向けに修正が必要でしたのでforkしました。情報を集積するデータベースとしてMySQLやPostgreSQL は使いたいのであれば使える(はず)ですが、テストはしていません。私としては、使わずに済ますつもりでいます。
さらに前提としてMeCabとpython-mecabも必要なのですが、こちらもPython3で動かすのに苦労したものの、なんとかなりました。ネットを検索すると他の方の足跡とともに多数の情報が見つかりますので、ここには書きません。必要でしたら探してみてください。
準備完了……? のつもりで次回予告
これで作りたいプログラムの前提になるモジュール類は揃ったと思いますので、ようやくトレンドツイートを収集して、学習して、なんとなく今はやりのツイートっぽい発言をするオウムの作成を目指そうと思います。
パロット ミニ ドローン ジャンピングスーモ 広角カメラ付 2輪型ロボット (ホワイト) 【日本正規品】
- 出版社/メーカー: パロット
- 発売日: 2014/08/29
- メディア: Wireless Phone Accessory
- この商品を含むブログ (1件) を見る
Raspberry pi 3でTwitter [RaspiParrot]
#おそるべしPokemon Go…… ってのはさておきまして。
目次
安普請だったのでもう少し堅牢に修正
昨日動かしたままにしておいたRaspiParrotなのですが、謎のエラーで停止していました。
2016-07-21 18:40:40,130 INFO ----- OneCycle開始 ----- 2016-07-21 18:40:40,131 INFO Twitter APIの使用を開始します。 2016-07-21 18:40:40,146 INFO Starting new HTTPS connection (1): api.twitter.com 2016-07-21 18:40:40,842 INFO 返答済みのMention:[2016-07-21 17:52:19] WindVoice_en 2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 16:21:50] WindVoice_en 2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 15:06:49] WindVoice_jp 2016-07-21 18:40:40,844 INFO 返答済みのMention:[2016-07-21 10:32:28] WindVoice_jp 2016-07-21 18:40:40,845 INFO 返答済みのMention:[2016-07-21 09:19:22] WindVoice_jp 2016-07-21 18:40:40,845 INFO ----- OneCycle終了 ----- 2016-07-21 18:43:40,945 INFO ----- OneCycle開始 ----- 2016-07-21 18:43:40,946 INFO Twitter APIの使用を開始します。 2016-07-21 18:43:40,962 INFO Starting new HTTPS connection (1): api.twitter.com pi@pi3:~/twitter$
なぜ謎のエラーなのかといいますと、記録が残っていないためです。上のとおり、ログは突然途切れています。サドンデスですね。これでは原因が突き止められないので、まずは情報採取をしっかりやりましょうということで改造します。
例外を処理
Pythonには「例外」という機能があります。何かまずいことが発生したときに、本来のプログラムの段取りとは別に、「突然ですがここで緊急ニュースです」とでもいった調子で始まる処理のことです。どんな「例外」が発生しうるのかはそのときやっている処理によるのですが、Python-Twitterの場合は、"TwitterError"という名称の「例外」が定義されており、これが発生(raise)します。下のリンクはソースコードです。ところどころにraiseという命令が置いてあります。
python-twitter/api.py at master · bear/python-twitter · GitHub
さて、例えばtwitter.api()を呼び出してkeyからアクセス権を取得するとき「アクセスしすぎでしょエラー」が発生することがあります。15分で15回以上アクセスすると、Twitter側でそれ以上の処理を拒否するようです。この場合、しばらく待ってリトライするのが正しい処理ということになりますね。
下のようにtry~catchを使用しました。しばらく待ってリトライするので、ログに残すだけでよいはずです。なお、発生した例外に対応する処理が作られていない場合、プログラムは強制的に終了させられます。RaspiParrotのような常駐プログラムでは、これは致命的ですので注意する必要があります。
def GetAPI(): ''' Twitter APIを準備する部分 ''' global inifile logger.info("Twitter APIの使用を要求") consumer_key = inifile.get("keys", "TWEETUSERNAME") consumer_secret = inifile.get("keys", "TWEETPASSWORD") access_key = inifile.get("keys", "TWEETACCESSKEY") access_secret = inifile.get("keys", "TWEETACCESSSECRET") encoding = 'utf-8' try: api = twitter.Api(consumer_key=consumer_key, consumer_secret=consumer_secret, access_token_key=access_key, access_token_secret=access_secret, input_encoding=encoding) except TwitterError as e: logging.warning(u"twitter.Api error: %s" % ( e.message )) except Exception as e: logging.warning(u"不明なエラー")
GitHub更新しました
更新したソースコードはGitHubに上げておきました。
github.com
Gitが、おもしろいほどわかる基本の使い方33〈バージョン管理、SourceTree、Bitbucket〉
- 作者: 大串肇,久保靖資,豊沢泰尚
- 出版社/メーカー: エムディエヌコーポレーション
- 発売日: 2015/05/26
- メディア: 単行本
- この商品を含むブログ (3件) を見る
Raspberry pi 3 + Python = Twitter Bot(Mentionをとりあえず返せるところまで)
アカウントを独立する
さて、前回までにとりあえずPython-Twitterモジュールの力を借りて起動するようになったTwitterクライアントですが、BotはBotらしくしたほうがいいのではないかということで、独立したアカウントを作成することにしました。
現在は、こちらのアカウントにRaspiParrotという名前でアプリケーションを登録しています。決まったことしかしゃべれないオウムのようなヤツなのでParrotという名前にしています。
ソースコードをひたすら書く
実装したい機能は、処理順に並べて大きく4つあります。
- 常時起動のプログラムで、定期的に動作を開始する
- 自分に届いたメンション(*1)を取得する
- 取得したStatesのそれぞれでループする。すでに返答を返したツイートも取得されるので、返答済みかどうかは判断する必要がある。
- まだ返答していないメンションを見つけたら何か応答を返す
(*1) 自分のアカウント名 @RaspiParrot という文字列を含んだツイートのことです
何からやるか?
下調べをした結果をもとに計画を立てました。実際には作りながら計画を立てている感じですが。
まず、自分に届いたメンションを取得するのはPython-TwitterのGetMentions()を使用すれば得られることがわかりました。
得られるのはメンションのリストなので、for文を使ってひとつひとつ確認することにします。メンションには、それが投稿された日時を示すcreated_atやcreated_at_in_secondsという要素を持っているので、いつのメンションまで返答済みなのかを記録しておいて、その時刻よりあとに届いたメンションに返答を返すことにします。
また、このプログラムは相当な回数再起動をすることになるので、どこまで返答済みかは、プログラムの外部に保存しておく必要があります(そうでないと何度も返答がきてうっとうしいことになるので)。コンフィグファイルを作成してそこに記録を残すことにしましょう。
返答を返すのはPostUpdate()というPython-TwitterのAPIに任せればよい…… と思ったのですがこれが大変でした(後述)。返答内容は、まずは簡易的にごあいさつを返すだけにしましょう。 @(相手のスクリーン名) + あいさつひとこと、でよいのですが、Twitterは同じ内容のメッセージを連投することはできない制限があるので、場当たり的ですが日時文字列を追加して重複メッセージを避けることにします。
今回のハマリポイント
なんでもない作業と思っていても、毎回何らかのポイントではまります。プログラムを書く力というのは結局はまったときに対処する方法をどれだけ持っているかですね。今回はPython-Twitterのバグでした。api.pyの中でstr()関数の変換のエラーがでています。モジュール内のエラーだと対処に困ります。ということで本家を探してみたところ、既知の問題のようです。
pi@pi3:~/twitter $ ./RaspiParrot.py Traceback (most recent call last): File "./RaspiParrot.py", line 86, in <module> main() File "./RaspiParrot.py", line 82, in main OneCycle() File "./RaspiParrot.py", line 62, in OneCycle ReplyMention(api, state) File "./RaspiParrot.py", line 77, in ReplyMention newstates = api.PostUpdate(u"@%s メンションありがとう" % ( state.user.screen_name )) File "/usr/local/lib/python2.7/dist-packages/twitter/api.py", line 949, in PostUpdate u_status = str(status, self._input_encoding) TypeError: str() takes at most 1 argument (2 given)
Error in UploadMediaChunked · Issue #345 · bear/python-twitter · GitHub
ここでは、対処済みということになっているのですが、私の手元では問題が再発します。該当のファイルが古いのだろうか、など調べてみたのですがそうでもなく…… 原因がよくわからなかったのですがPython 2.7の互換性の問題だという説明があったので、やむをえずPython3を使って動かすことにしました。
Raspberry piにはPython3がインストール済みでしたが、Python-Twitterは2.7にインストールされていたので、インストールし直しになりました。
- /usr/bin/pythonがpython27へのシンボリックリンクになっていたのでいったん削除
- python3へのシンボリックリンクを/usr/bin/pythonにリンクしなおし
- Python-Twitterをインストール
これでなんとか動くようになりました。
ソースコード
というわけで、できたのがこちらのソース。
同じ物をGitHubにも置いておきます。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import twitter import time import logging import configparser CONFIGFILE="config.ini" def init(): ''' 最初に1回だけ処理する部分 ''' # 設定ファイルの準備 global inifile inifile = configparser.ConfigParser() inifile.read(CONFIGFILE) # ログファイルの準備 global logger logging.basicConfig(filename=inifile.get("log","filename"), level=logging.INFO, format='%(asctime)-15s %(levelname)s %(message)s') logger = logging.getLogger("RaspiParrot") def GetAPI(): ''' Twitter APIを準備する部分 ''' global inifile logger.info("Twitter APIの使用を要求") consumer_key = inifile.get("keys", "TWEETUSERNAME") consumer_secret = inifile.get("keys", "TWEETPASSWORD") access_key = inifile.get("keys", "TWEETACCESSKEY") access_secret = inifile.get("keys", "TWEETACCESSSECRET") encoding = 'utf-8' return twitter.Api(consumer_key=consumer_key, consumer_secret=consumer_secret, access_token_key=access_key, access_token_secret=access_secret, input_encoding=encoding) def OneCycle(): ''' 定期的に、繰り返し処理する内容 ''' global inifile logger.info(u"----- OneCycle開始 -----") LastMentionSeconds = int(inifile.get("records", "LastMentionSeconds")) api = GetAPI() try: MaxMentionSeconds = 0 for state in api.GetMentions(): logging.debug(u"LastMentionSeconds:%s" % ( LastMentionSeconds )) logging.debug(u"created_at_in_seconds:%s" % ( state.created_at_in_seconds )) t = time.strftime(u"%Y-%m-%d %H:%M:%S", time.localtime(state.created_at_in_seconds)) if LastMentionSeconds < state.created_at_in_seconds: logger.info(u"新しいMentionが到着:[%s] %s" % (t, state.user.screen_name) ) # このMentionは未処理なので応答を返す ReplyMention(api, state) if MaxMentionSeconds < state.created_at_in_seconds: MaxMentionSeconds = state.created_at_in_seconds else: logger.info(u"返答済みのMention:[%s] %s" % (t, state.user.screen_name) ) # メンション応答があった場合はLastMentionSecondsを更新 if MaxMentionSeconds != 0: inifile.set("records", "LastMentionSeconds", str(MaxMentionSeconds)) inifile.write(open(CONFIGFILE, 'w')) except UnicodeDecodeError: print("Your message could not be encoded. Perhaps it contains non-ASCII characters? ") print("Try explicitly specifying the encoding with the --encoding flag") sys.exit(2) logger.info(u"----- OneCycle終了 -----") def ReplyMention(api, state): ''' Mentionが届いている。何か気の利いた返答をしよう。 ToDo: ちっとも気が利いていないので何か考える ''' logger.info(u"Mention返しを開始") t = time.strftime(u"%Y-%m-%d %H:%M:%S", time.localtime()) message = u"@%s メンションありがとう[%s]" % ( state.user.screen_name, t ) newstates = api.PostUpdate(message, in_reply_to_status_id=state.id) def main(): ''' メインルーチン ''' init() while True: OneCycle() # Twitter API Rate Limitにより、15回/15分の制限がある time.sleep(180) # おまじない if __name__ == "__main__": main()
動作します
今のところごく簡単なありがとうメッセージを返すだけです。フォローする必要はありません(しても、何も起こりません)。@RaspiParrotにメンションが届くと、こんな簡単なメッセージが返ってきます。開発中でなければ、動き続けているはずです。
今後
あとはなんといっても気の利いた返答ですね。ここがアイディアの絞りどころなわけですが…… できそうなことで何かあるかなぁ。検討中です。
- 出版社/メーカー: Parrot
- 発売日: 2016/03/24
- メディア: Camera
- この商品を含むブログを見る
Python Twitterのメモ
Python-Twitterの細かい説明が無くソースコードを読んだりTwitter APIの規格を読む必要があります。
ちょっとメモのため残しておきます。
Modules Documentation — python-twitter 3.1 documentation
Status
Tilelineを取得したときなど、ツイートのひとつひとつはStatusと呼ばれる単位で管理されています。これに含まれる情報の一覧です。
AsDict | このオブジェクトの辞書形式表現 |
---|---|
AsJsonString | このオブジェクトのJSON文字列形式 |
NewFromJsonDict | |
contributors | このアカウントをcontributorsとして運営するユーザの一覧 |
coordinates | ユーザやアプリによって報告された緯度経度など |
created_at | ツイートされた時間。世界標準時(UTC)で表記 |
created_at_in_seconds | ツイートされた時間を秒数で表記 |
favorite_count | likeされたおおよその回数 |
favorited | このユーザによってlikeされたかどうか(true/false) |
geo | 使わない。corrdinatesを代わりに使用する |
hashtags | |
id | このツイートにつけられた固有のID |
id_str | idの文字列表現 |
in_reply_to_screen_name | このツイートがリプライの場合、元ツイートを書いた人のスクリーン名 |
in_reply_to_status_id | このツイートがリプライの場合、元ツイートのID |
in_reply_to_user_id | このツイートがリプライの場合、元ツイートを書いた人のユーザID |
lang | 機械的に検知されたこのツイートの言語(BCP47形式) |
location | |
media | |
param_defaults | |
place | このツイートが関連づけられた場所 |
possibly_sensitive | このツイートが(sensitive)と判断されたかどうか(true/false) |
retweet_count | リツイートされた回数 |
retweeted | このユーザによってリツイートされたかどうか |
retweeted_status | |
scopes | プロモーションツイートで使われる模様 |
source | |
text | このツイートのテキスト(UTF-8形式) |
truncated | ツイートは140文字で短縮されたかどうか |
urls | |
user | このツイートをポストしたユーザについての情報 |
user_mentions | このツイートでメンションされているユーザのIDとScreenName |
withheld_copyright | DCMAの申し立てで保留されているかどうか(true/false) |
withheld_in_countries | 保留されている国を表す2文字のコードのリスト |
withheld_scope | 保留の範囲(userまたはstatus) |
Twitter公式の説明を参考にしました。
dev.twitter.com
User
ついでにuserの内容についても書いておきます。
created_at | ユーザアカウントの作成日時 |
---|---|
description | ユーザの自己紹介文 |
favourites_count | likeの数 |
followers_count | フォロワー数 |
friends_count | フォロー数 |
id | アカウントのID番号 |
lang | 言語 |
listed_count | このユーザがリストされている数 |
location | 場所 |
name | スクリーンに表示される名前 |
profile_background_color | |
profile_background_image_url | |
profile_image_url | ユーザのアイコン |
profile_link_color | |
profile_sidebar_fill_color | |
profile_text_color | プロファイルのテキスト |
screen_name | ログインID |
statuses_count | ツイート数 |
time_zone | タイムゾーン |
utc_offset | 世界標準時からの時差(日本は32400秒(+9時間)) |
Raspberry pi 3 + Python = Twitter Botの起動(とりあえず投稿するところまで)
Raspberry piの良いところは常時起動しても電気代があまりかからないところです。Raspi 3になって消費電力はやや増えましたけれども、それでもスマホより少し少ないくらいなので、24時間稼働していても心が痛みません。
常時起動の良さを活かしてTwitterのBotにしてみようということで、今回は起動部分を作ってみます。今回の作業はとっかかりのところだけですが、このくらいなら1時間ほどあればできるのでお手軽です。
Python Twitterモジュールのインストール
Twitterはユーザが凄く多いですから、自分で作らなくても配布されているものがたくさん見つかります。GitHubを探したらたくさんForkされているモジュールがあったので、これをお借りすることにしました。顔写真は作者さんなのでしょうか…… 熊……
インストールはsudo pipを使えば簡単です。
pi@pi3:~/twitter $ sudo pip install python-twitter Collecting python-twitter Downloading python-twitter-3.1.tar.gz (80kB) 100% |????????????????????????????????| 81kB 1.2MB/s Collecting future (from python-twitter) Downloading future-0.15.2.tar.gz (1.6MB) 100% |????????????????????????????????| 1.6MB 154kB/s Requirement already satisfied (use --upgrade to upgrade): requests in /usr/lib/python2.7/dist-packages (from python-twitter) Collecting requests-oauthlib (from python-twitter) Downloading requests_oauthlib-0.6.2-py2.py3-none-any.whl Collecting oauthlib>=0.6.2 (from requests-oauthlib->python-twitter) Downloading oauthlib-1.1.2.tar.gz (111kB) 100% |????????????????????????????????| 112kB 1.2MB/s Building wheels for collected packages: python-twitter, future, oauthlib Running setup.py bdist_wheel for python-twitter ... done Stored in directory: /root/.cache/pip/wheels/22/1e/2e/506871fa7dc610616948e70812d5e2518cd89c13f757b98f6c Running setup.py bdist_wheel for future ... done Stored in directory: /root/.cache/pip/wheels/11/c5/d2/ad287de27d0f0d646f119dcffb921f4e63df128f28ab0a1bda Running setup.py bdist_wheel for oauthlib ... done Stored in directory: /root/.cache/pip/wheels/e6/be/43/e4a2ca8cb9c78fbd9b5b14b96cb7a5cc43f36bc11af5dfac5b Successfully built python-twitter future oauthlib Installing collected packages: future, oauthlib, requests-oauthlib, python-twitter Successfully installed future-0.15.2 oauthlib-1.1.2 python-twitter-3.1 requests-oauthlib-0.6.2 You are using pip version 8.1.1, however version 8.1.2 is available. You should consider upgrading via the 'pip install --upgrade pip' command. pi@pi3:~/twitter $ pip install --upgrade pip Collecting pip Downloading pip-8.1.2-py2.py3-none-any.whl (1.2MB) 100% |????????????????????????????????| 1.2MB 201kB/s Installing collected packages: pip Found existing installation: pip 8.1.1 Uninstalling pip-8.1.1: Exception: Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/pip/basecommand.py", line 209, in main status = self.run(options, args) File "/usr/local/lib/python2.7/dist-packages/pip/commands/install.py", line 317, in run prefix=options.prefix_path, File "/usr/local/lib/python2.7/dist-packages/pip/req/req_set.py", line 726, in install requirement.uninstall(auto_confirm=True) File "/usr/local/lib/python2.7/dist-packages/pip/req/req_install.py", line 746, in uninstall paths_to_remove.remove(auto_confirm) File "/usr/local/lib/python2.7/dist-packages/pip/req/req_uninstall.py", line 115, in remove renames(path, new_path) File "/usr/local/lib/python2.7/dist-packages/pip/utils/__init__.py", line 267, in renames shutil.move(old, new) File "/usr/lib/python2.7/shutil.py", line 303, in move os.unlink(src) OSError: [Errno 13] 許可がありません: '/usr/bin/pip' You are using pip version 8.1.1, however version 8.1.2 is available. You should consider upgrading via the 'pip install --upgrade pip' command. pi@pi3:~/twitter $ sudo pip install --upgrade pip Collecting pip Downloading pip-8.1.2-py2.py3-none-any.whl (1.2MB) 100% |????????????????????????????????| 1.2MB 201kB/s Installing collected packages: pip Found existing installation: pip 8.1.1 Uninstalling pip-8.1.1: Successfully uninstalled pip-8.1.1 Successfully installed pip-8.1.2 pi@pi3:~/twitter $
Exampleを入手するためにソースコードもclone
pipでインストールすると、exampleは含まれていません。exampleの入手や、今後の開発の目的でソースも入手しておきます。
pi@pi3:~/twitter $ git clone https://github.com/bear/python-twitter/ Cloning into 'python-twitter'... remote: Counting objects: 2954, done. remote: Total 2954 (delta 0), reused 0 (delta 0), pack-reused 2954 Receiving objects: 100% (2954/2954), 7.24 MiB | 1.36 MiB/s, done. Resolving deltas: 100% (1873/1873), done. Checking connectivity... done. pi@pi3:~/twitter $
Twitterのアカウントに開発者登録する
さて、いまやろうとしていることは、既存のTwitter Botを利用者として使うということではなくて、自作の(とはいえソースのほとんどは借り物ですが)Botを動かそうということです。こういったときには、Twitter社に自分のプログラムを登録して、アクセス権をもらう必要があります。このため自分のTwitterアカウントを登録します。
私はこれまでTwitterアカウントに携帯電話の番号を登録していなかったのですが、開発者登録するときには必須となっていました。私のアカウントのうち、ほとんど使われていなかったWindVoice_enのほうにプログラムを登録しました。
開発者登録の方法は、上でソースをもらったbearさんのページに記載があるのでそれを参考にすればOKです。自分で作ろうとしているアプリケーションの名称、簡単な説明、アプリを登録しているURL(作成中の場合はとりあえず何でも埋めておけばいい(spaceholder)ようです)を記載すれば良いことになっています。
登録をすると、コンシューマーキー、コンシューマーシークレット、アクセストークン、アクセストークンシークレット、という4種類の鍵文字列がもらえます。これらは大事な情報なので他の人に知られてはいけません。パスワードに相当するものですね。以下の説明ではxで情報をつぶしてあります。
テストツイート
cloneしたソースコードのexample/tweet.pyがつぶやきを投稿するためのサンプルプログラムです。投稿したい文字列と一緒にアクセスキー4個を入力する必要があるのですが、長くて面倒なので、環境変数に登録してしまうことにしました。それができる機能がすでに織り込まれています。
pi@pi3:~/twitter/python-twitter/examples $ tail ~/.bashrc fi eval $(thefuck --alias) # Env for Raspi3Bot export TWEETUSERNAME=xxxxxxxxxxxxxxxxxxxxxxxxx export TWEETPASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx export TWEETACCESSKEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx export TWEETACCESSSECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx pi@pi3:~/twitter/python-twitter/examples $ pi@pi3:~/twitter/python-twitter/examples $ . ~/.bashrc
ホームディレクトリの.bascrcの末尾のところにこんな感じで4つの鍵を書いておきます(.bashrcはworld readableなので、アクセス権的に不安ですけど、このラズパイを使うのは私だけということで……)。これを記載したら、.bashrcを再読込させて、いよいよ投稿を試してみます。
pi@pi3:~/twitter/python-twitter/examples $ ./tweet.py "もう一度コメントに挑戦……" WindVoice_en just posted: もう一度コメントに挑戦…… pi@pi3:~/twitter/python-twitter/examples $
念のため自分のタイムラインを確認すると確かに投稿されていました。
まとめ
とりあえず投稿するところまでいけましたが、目的はコマンドラインからつぶやくことではなくて、これで常時起動のBotを作ることでした。場当たり的ですがここからがアイディアの出し所ですね。過去の投稿と合わせればセンサーからの読み取り値をつぶやくなどはすぐできると思いますが、しかし公に知らせて楽しい物でもないかも…… どうしたものか少し考えます。
Raspberry Pi3 Model B ボード&ケースセット (Element14版, Clear)-Physical Computing Lab
- 出版社/メーカー: TechShare
- メディア: エレクトロニクス
- この商品を含むブログ (2件) を見る
誰でもできるTwitter Botの作り方―人気キャラにつぶやかせる
- 作者: 鏑家経済研究所
- 出版社/メーカー: イーグルパブリシング
- 発売日: 2010/07/23
- メディア: 単行本(ソフトカバー)
- 購入: 7人 クリック: 569回
- この商品を含むブログ (15件) を見る
Virtual Reality:とりあえずローエンドコース試しました
前回の日記で書いたような仮想現実環境は、PCから買い直しになるわけで、そう簡単に購入というわけにもいきません。コメントでMicrosoftのHoloLenzを紹介頂きましたが、限定的に販売されているようでどうも手に入れるのは無理そうです。
しかし手軽なのを探してみると、簡易なものも出回っていました。案ずるより産むが易しということで「とりあえず」手に入れてしまいましたよ。Tepoinn VR Shineconというゴーグルです。
類似した商品がいくつもAmazonに掲載されているのですが、これがランキング1位だったので、という安易な理由で注文しました。値段は3000円。初めて体験するにはこのくらいの値段の方が手を出しやすいですよね。
(テポインー)Tepoinn® 3D VRメガネ 超3D映像効果 3.5- 5.5インチのスマートフォンに適用
- 出版社/メーカー: Tepoinn
- メディア: エレクトロニクス
- この商品を含むブログを見る
開梱
内容物は本体と、ぺらっとした紙のマニュアルと、スタイラス2本。あとで書きますが、VR体験中は画面をタッチすることがほとんど不可能なので、このスタイラスはどういうシチュエーションで使うことを想定して同梱されているのか不明です。本体はこんな感じ。
本体上部のツメからフロントベゼルを外すことができます。外すとそこにiPhone等をセットするための場所があります。スマホを挟み込めるようにバネが仕込んであり、説明によれば3.5インチから6.0インチまでの画面サイズに対応しているそうです。私はiPhone6sを持っていますのでこれをセットすれば本体側は準備完了です。
なお、正面のVR SHINECONと書かれている部分は取り外すことができます。そうするとスマホのカメラレンズ部分が露出するので、部屋の様子を映せば拡張現実用途にも使えるようになっています。
上の写真は裏側です。私は眼鏡を使っているのですが、眼鏡をかけながらゴーグルをかぶるのは無理でした。また、鼻があたるところは結構大きめに隙間があいている(私の鼻が低いのか?)ので、外から光が漏れてきます。部屋を暗くするか、ここになにか詰め物でもすると良いと思います。顔に当たる部分のクッションは適度に柔らかく触り心地のいいものです。没入感を邪魔しない良い作りだと思いました。
両目で立体視する場合見やすいように調整する必要がある(そうでないとめまいがして気持ち悪いと思います)わけですが、このゴーグルでは2カ所調整ができるようになっています。ひとつは視差を調整するためのもので、眉間のあたりにダイヤルがあります。これを調整すると2個あるレンズが寄り目になったり離れたりします。もうひとつはスマホ画面との距離を調整するもので、こめかみのあたりにダイヤルがあります。調整できる範囲は1cmくらいですが、それでも結構違いを感じます。
テスト(なごみの耳かきVR)
この製品には専用のソフトは付いていません。世の中にあるVR関係のアプリを使ってね、ということのようです(そういう説明もありませんが)。App Storeで探してみたところ、真っ先に見つかったのがこれでした。
公式サイトにあるプロモーションムービーはこちらですね。というか基本的にこれだけしかない、単機能といってよいソフトのようです。
いえ、探したわけじゃ無くてホントにこれが最初に見つかったんです。ホントなんです…… っていいわけはさておきまして、インストールしてみました。どういうものかは動画を見ればわかると思いますので技術的な感想を。
できることできないこと
できること
驚くほどちゃんと立体視できます。障子の向こうでひらひら舞う落ち葉、近くにある茶器や床の間、女の子との距離感(見る角度によりポリゴン突き抜けることがありますが)が違和感なく見えます。音はスマホのスピーカーではなくイヤホンを推奨します。耳かきしてもらうというシチュエーションなのですが、右耳を下にするか左耳を下にするかで、ゴソゴソいう効果音や、声の聞こえてくる方向が変わります(よくできてますが、こういうところがしっかりしていないと没入感でませんよね)。
スマホのセンサが非常に精度のよいものなので、首を少し傾けたり振り向いたりしても即座に画面が付いてきます。ラグは感じません。あちこちよそ見をして楽しめます。体験そのものは短いです。5分くらいでしょうか? 無料のソフトですので不満はありません。よくできているなぁと素直に関心しました。
できないこと
気がついたことがいくつかあります。
- まず、移動は検知されません。検知されるのは方向と角度だけです。例えば女の子に近づこうとして身を乗り出しても、自分の位置は変わりません。そのあたりは、首を傾ける ⇒ 女の子の膝が近づいてくる、というようにプログラム的に作られているようでした。
- 次に、画面をタップできません。目の前にあるのですから当然と言えば当然ですが、スマホに直接触れないので、他のボタンを含めて操作する方法がありません。この問題を解決する方法が実装されていました。最後に空中に「終了しますか?」というようなUIが空中に浮かんでみえるのですが、「はい」「いいえ」のどちらかの選択肢を画面の真ん中に持ってきて少し維持すると、その選択肢が選ばれる、という仕組みになっていました。最初セットアップするときに「ゴーグルのふたを閉じてしまったらどう操作するんだ?」と疑問に思っていましたが、この解決方法はなかなかうまいと思いました。
そのほか感想
- Google cardboardというゴーグルが売られているようです。
Google Cardboard I/O 2015 ハコスコ社製
- 出版社/メーカー: ハコスコ
- メディア: エレクトロニクス
- この商品を含むブログ (2件) を見る
- YouTubeの360対応動画などもあり、スマホが一般に広く使われていることや手軽に試せる値段などを考えると、本格的なVR向けハードウェアよりもちらの方がずっと先に普及しそうです。しかし、映画1本をこのゴーグルでみるかというと、おそらくバッテリーが持たないため無理と思います。マニュアルには、充電しながら使わないこと、と注意が書かれていました(過熱の恐れがあるのだと思われます)。
- 操作が不便なのだけが難点ですが、イヤホンに付いているボリュームボタンとか「次の曲に進む」「戻る」ボタンを前提にしてアプリを作ってみる手もあるかもしれません。専用コントローラを前提にするのは、スマホを使う手軽さとはトレードオフになってしまうと思います。もしくは、ひょっとして声で操作するアプリの可能性はあるでしょうか。処理能力的に追いつくかどうかですが、簡単なキーワード操作くらいならなんとかならないかな。いずれにしても、スマホが大きく普及した要因であるタップやスワイプなどの手軽な操作を使えないのは、痛いところです。
まだ手元に届いたばかりですので、もう少し遊ぶ余地があります。対応アプリなど探してみようと思います。
(テポインー)Tepoinn® 3D VRメガネ 超3D映像効果 3.5- 5.5インチのスマートフォンに適用
- 出版社/メーカー: Tepoinn
- メディア: エレクトロニクス
- この商品を含むブログを見る
おまけ。Clash of ClansのYouTube動画。これもゴーグルでみると360度見渡せます。
Clash of Clans 360: Experience a Virtual Reality Raid