ツイートを長期間収集する実験(プログラム準備編(1))

# 注意事項の追記(2022年5月時点)
最初に作成したときから既に7年とか経ちそうな割に、未だにLGTMとかされているため、念のために追記します。
**本稿記載のAPIは、現時点で既に存在しないAPI(2018/08に廃止)です。**
代替として、「/statuses/filter」を用いる手段がありましたが、こちらも2020年10月末の廃止が宣言されました。
API 2.0の「/2/tweets/search/stream」が使えるようですが、こちらについては調べていません。
私自身は、「/search/tweets」を使って遡って取得する方向に逃げました。詳細は[こちら](https://qiita.com/yuhkan/items/255553a09b5c50840759)。

# 前回まで

- [x] とりあえず要求仕様(?)は決まった。<br>
    * **10月末までに、1月中旬までTwitterの特定ワードを含むツイートすべて取得するプログラムを作る**
    * 手持ちの機材使うので、ひとまずクラウドは無し。Dockerとか組み合わせて実行環境作りませう。
    * DBは**MongoDB**を用いる(仮)。JSON加工無しにぶち込んで後から計算するなら問題なし。
    * プログラムは今後を踏まえて**Python**にしよう。先行者も多いことだし。 
- [ ] じゃあひとまずプログラム作るべー<br>
    * 環境整えて、一番基本になるプログラムからいってみよー　<- New!

# まず考えること
　やることは決まっていて、足回りも決まっていているなら、まずは**ググることから**始めちゃうのが自分流です。
　要するに、「**自分がやりたいと思ったことの8割は、誰かが先行してやっている**」というヤツです。
 　Qiitaの中だけでも、<a href="http://qiita.com/search?utf8=%E2%9C%93&sort=&q=tag%3Apython+tag%3Amongodb+tag%3Atwitter">同じようなコト(というか、より高度なこと)をやっている方がいて</a>、大いに参考にさせていただくしかないのですが、残念ながらそのものずばり、という事例はなし。Stack Over Flowとかも調べた範囲では、要求仕様をちゃんと満たせるプログラムをまんま公開してるような事例はありませんでした。
　あったら楽だったのになぁ……<sub>ちっ</sub>
 
 * 残念なことにPublic Stream使っていないとかの事例が多い。
 * 切断(特に予期しない、または向こう側(Twitter)からの切断)からの再接続について考慮されてるものが少ない

　スクリプトコピペしまくって形にする、~~スクリプトキディ的な~~個人的使用の範囲で要件を充足するものを作るにしても、この辺は自分でまとめる必要がありそうです。
 
## そもそもなんでPublic Stream？
　Twitterからのデータの取得は大まかに2種類有り、「**REST API**使用」と、「**Streaming API**使用」の2種類があります。
 　**REST**というのがメッセージを送信して、その結果を返してもらう、を繰り返す手段であるのに対し、**Streaming API**は、一度命令を送信したら延々そこに対して結果を送ってくる、というものです。
  (と、理解してます。<a href="https://dev.twitter.com/overview/documentation">詳細は文献に当たって確認してください。</a>)

　そもそもなんで Public Steram にするか、というと、
   1. 想定される流量が、REST形式で取得可能な限界を超える可能性が大いにあった
   2. <a href="https://www.amazon.co.jp/dp/477414732X">Twitter API ポケットリファレンス</a>を買ってきて読む限り、(読むことだけ想定するなら)RESTでやるのは意外とめんどくさそう。

　ぶっちゃけ、**なんか適当なライブラリ持ってきてリードだけするなら、Stream APIの方がRESTでやるよりも面倒が少なそう**という大変怠惰な理由です。送られてきたものをDBにぶち込み続けるだけで済むはずなので。
 
 　Stream APIにも複数あって、**「ガチにTwitterに流れる全部(要契約)」**、**「自身のタイムライン上のツイート」**、**「全体の1%をランダム」**、**「全体に対してキーワード・ロケール等で絞った結果」**まで複数ありますが、ここでは最後の「**<a href="https://dev.twitter.com/streaming/reference/post/statuses/filter">指定の検索ワードで絞ったもの</a>**」を使います。Public Stream というとこれらの総称っぽい使い方のようですが、その辺はひとまずおいといて。
 <small>(ちょっと調べただけなんですが、Filferで取得出来るツイートは、全体1%、ではなく全部、という理解で良いんですかね？)</small>
 
## 切断と再接続
　ロートル人間だからでしょうか、Stream APIがHTTP接続を維持する、といわれてもびっみょーに信頼できないというか、**絶対に何事も無くても切断される。むしろ何も問題が無いからこそ切断される**と警戒してしまう訳です。
　先の参考書(ポケットリファレンス)を見ても、何かあったら切断はされるものだから再接続は想定する必要がある、的な書き方をされています。
 　……が、著名なライブラリのページを見てみても、自力で実装している例を見ても、ざっと見渡した範囲で*切断後の再接続について記載してるページが無い。*
 　……APIでそこまでやってくれるんけ？　だとしちゃー、これほど楽なことは無いのですが……
  
(わかっている人向けの注：コーディングしつつ、思い出しつつで脳内時系列に沿って書いてますので、ひとまずツッコミはお待ちください)
  
## ライブラリの選定
　いろいろ考えることは多いのですが、~~コピペしまくってお茶を濁す~~先達の素晴らしいコードを参考にする、という観点で日本語資料の多いTwitter接続のライブラリを検討することに。
 * 当然Stream APIに対応している必要がある
 * ググって速攻答えが出てくるのが理想。
　……とかを踏まえて、<a href="http://www.tweepy.org/">Tweepy</a>を利用することに。

　データベース側についても同様の観点でライブラリを探したところ、
 * とにかくコーディングの記述量が少なくて済む
 * ググればだいたい回答が見つかる。
 * MongoDBオフィシャルとか、そういうのに近いものっぽい
　という理由で<a href="https://api.mongodb.com/python/current/">PyMongo</a>を使う事に決定。
 
 　がっちがちの鉄板？　そりゃあシロートがどうにかしないとアカンいうなら定番使うしか……
  
# 開発環境とか。
　何を使うかを決めたとして、じゃあ開発環境、およびテスト環境を用意しましょう。
　私はVBから入ってC → VC++ → C# とやってきたガッチガチのWin屋です。開発に使える環境も当然Windowsですので、リリース=実施直前まではWin上で開発できるのが理想であるのは言うまでもありません。
 というか、IDE(統合開発環境)がないと基本即死です。ましてやLinux上でどうにかせぇ言われたら、土下座するしかありません。
 
 　幸い、スクリプト言語なので環境依存性はたいしたことはなく、昨今はライブラリ等にしてもインストールが自動化されていることもあり、大昔に比べれば苦労はだいぶ減っている……筈です。

* Python とその周辺ライブラリを一括インストールする手段として、<a href="https://www.continuum.io/downloads">Anaconda</a>を利用。環境変数だのまで全部お任せインストールで問題なし。
* MongoDBどうすんべー、と一瞬考えたものの、<a href="https://www.mongodb.com/download-center#community">Windows版が存在する</a>のでそれでいいや、と言う事に。<br>最初は<a href="https://docs.docker.com/docker-for-windows/">Hyper-Vも使えるDocker for Windows</a>上に立てる、というも考えたものの、実際試すと<a href="https://github.com/docker-library/mongo/issues/107">データの永続化で躓く</a>上に解決策も失敗するので、~~めんどくさいし~~Win版でいいや、と言う事に。

　インフラ周りはこれでいいとして、やっぱIDE使いたいよねー、とか~~世の中なめた~~使い慣れた環境に未練を残してググっていたところ、<a href="https://microsoft.github.io/PTVS/">Python Tools for Visual Studio</a>なんていうすんばらしいものが。**しかもAnacondaとかのWin版Python呼んで、その場で簡易デバッグできる**。
 　もはやこれしか無い、という理由により、
  * Anaconda (Python 3.5.2 :: Anaconda 4.1.1 (64-bit) )
  * MongoDB for Win(MongoDB shell version: 3.2.10)
  * VisualStudio2013 + Python Tools For Visual Studio(Ver.2.2.2)
  * Tweepy + PyMongo(取得時の最新)
  
  　という構成で開発・テスト実施することに決定。懸念としては、

1. 本来実行環境での実行で問題は出ないか。環境に依存する部分はないか。
2. 開発環境で十分なテストが出来るか。想定し得ない問題は発生しないか。
3. 開発環境と実行環境でインフラ周りのバージョンが変わる可能性

　あたりですが、……まあ、どっちにしても最後は実機テスト必要だし、よほどの書き方しなければ問題は発生しない、筈、ということでひとまず放置。
 (これがお仕事なら、そこら辺みっちり詰めないとあかんでしょうが……)
 
# プログラムの要求仕様
　今回作るプログラムは、**動かし始めたら何が何でも3ヶ月動かし続け、止めることまかりならん**という鬼の掟を背負っています。よって、**必須の機能に絞って実装し、それ以外は別の手段でなんとかする**ことにします。

#### 最優先で実装する機能
* TwitterからのPublic Stream 受信
* 受信したデータのMongoDBへの格納
* 不意の切断時に再接続を実施する機能

#### 第二優先として実装する機能
* 接続、切断、再接続など、事象が発生した場合にその旨を記録する機能
* 上に加え、TwitterのDirect Messageで、事象発生を通知する機能(連絡くらいはほしいという親心)

#### 可能なら実装する機能
* 1日の流量やストレージの残容量などについての通知。

#### 余力があれば検討する機能
* 特定の日付日時になったら自動で終了する

#### 要求仕様に含めない機能
* ブラックリストによる特定アカウントはじき機能(プログラム内の機能として)。
* リアルタイム解析を想定した、ツイートデータの加工(プログラム内の機能として)

　まあ、こんなものでしょう。優先度の高い要素から突っ込んでいって、少しずつ完成度を高めていくようにしましょう。
 
# とりあえずTwitter受信プログラム
　「**オライリーのPythonチュートリアルを手元に置く**」「**わかんなかったらググる**」の誓いを胸に、とりあえず実証プログラムの作成開始。Visual Studioでプロジェクトの新規作成から、「Python Application」を指定すれば、なじみのエディタでPythonのコードを書く準備ができあがります。これは便利。
 　チュートリアル的なコードを打ったら、[F5]で実行。C言語のコンソールアプリと同じ手順で実行まで試せるので、本当にストレスがありません。ステップ実行出来ないのが残念だなー。
 
 　さて、まずTwitterとお話しできなければ論外なので、その辺作るところから始めましょう。
  　最初にTweepyを導入する必要があります。Anaconda Promptから「pip」コマンド打つのかなー、とかおもったら、これもVisual Studio上から実行可能。
  1. メニューバーの[表示]→[その他のウインドウ]→[Python Environments]を選択して表示。
  2. [Python Environments]ウインドウの中央ドロップダウンから、[pip]を選択。
  3. [Search PyPl and installed packages]のテキストボックスに「tweepy」と入力。
  4. 『"pip install tweepy" from PyPl』をクリック。で、インストール完了。

　NuGet並の楽さっすね。

　とりあえずググって見つかるいくつかのページや、<a href="http://docs.tweepy.org/en/v3.5.0/streaming_how_to.html">本家Tweepyのチュートリアル</a>を元に、Stream APIでツイートを取得するプログラムを書き書き。

```Python:tweetCheck.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tweepy
#Twitter API 実行に必要な変数は、自分で用意する。
CK = ''   # Consumer Key
CS = ''   # Consumer Secret
AT = ''   # Access Token
AS = ''   # Accesss Token Secert

class Listener(tweepy.StreamListener):
    def on_status(self, status):
        print(status.text.encode('shift_jis', 'ignore'))
        return True

    def on_error(self, status_code):
        print('エラー発生: ' + str(status_code))
        return True

# ここからメイン処理
auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AT, AS)     #アクセストークンの取得

listener = Listener()                       # Listenerクラスのインスタンス
stream = tweepy.Stream(auth, listener)      # ここから受信開始。

#どれか選択の上コメントアウト外す。
#stream.filter(track=['#xxxxxx'])  # 指定の検索ワードでフィルタ
stream.sample()                    # Twitter全ツイートから1%ピックアップ
#stream.userstream()               # ユーザ自身のTL
```

　……え、31行(空行、コメント含む)？　こんなんでなんとかなるの？？
 と思いつつ実行。
 ![実行結果](https://qiita-image-store.s3.amazonaws.com/0/139416/d64d961e-f8a9-a972-fd4a-a70bf68c0c26.png)
 　読めない(UTF-8なので)ですが受信は出来てます。終了はCtrl + Cで強制終了。
  
#### 注意事項
さくっと動いた感じですが、実際には2箇所ほど詰まりました。
* 1行だけ表示されるがすぐに強制終了した<br>　　→Visual Studioでまず作られるpyファイルの文字コードが「Shift-JIS」だった。<br>　　　[メニュー]→[ファイル]→[保存ファイルの詳細設定]から、UTF-8を選択して保存。
* 上を直した後実行したら、一瞬表示がされるが強制終了する。タイミングはランダム。<br>　　→「def on_status(self, status):」の下のprintが、当初「print(status.text)」だったが、<br>　　　<a href="http://lab.hde.co.jp/2008/08/pythonunicodeencodeerror.html">コマンドプロンプトに表示出来ない文字を表示しようとして死ぬのだとか。</a><br>　　　エンコードを変換するようにしたら、化けまくってはいるが表示はされるようになった。

　前者は最初の1回直せばオッケー。後者は、常に動かしてるなら表示する必要無いからこれもオッケー。
 Python初めて数日でこの程度まではなんとかなる、というのならば意外と10月末に間に合うかも知れません。
 
 　次回はこのソースに肉付けしていきます。
  (続く)