学習するTwitterbotの作り方

# 前置き
初めましてぺねるてと申します。Qiitaに記事を書くのも何かものを作るのも初めてなので

- コードの読みやすさ・簡潔性・正しさ
- 日本語

等おかしなところがあるかもしれませんがその際はコメント欄で優しく教えてくださるとありがたいです。

この記事は

- Tweepyでツイートを取得し
- janomeでランダム文を生成し
- cronで定期自動ツイートを行う

ことを目的としています。

#TwitterAPIKeyの取得
Tweepyを動かすためにはまずTwitterAPIKeyの４つのKeyが必要になります。それぞれの役割は以下の通りです。

|        名前                 |役割                                           |
|:-----------:|:----------------------------------|
|Consumer Key(API Key)| アプリケーションAPIKey　アプリケーションごとに付与されるID   |
|Consumer secret(API secret)|アプリケーションAPIのシークレット|
|Access Token|それぞれのユーザーがアプリ用に発行されるアクセストークン　これが紐づけられているユーザーはアプリ側から操作できる|
|Access Token Secret|アクセストークンのシークレット|

この４つのKeyを取得するためにはTwitterAPIを申請する必要があります。申請の流れは[こちら](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)に詳しく載っているので参照してください。

#Tweepyによるツイートの取得
4つのKeyが取得できたらpythonでコードを書きます。とりあえず新しいツイートを手元に取得できるプログラムsyutoku.pyを作ります。Tweepy等は予めpip等でインストールしておいてください。

``` python:syutoku.py
import tweepy
import csv

def get_oauth():
    CONSUMER_KEY = 'うんたらかんたら(取得したやつ)'
    CONSUMER_SECRET = 'うんたらかんたら(取得したやつ)'
    ACCESS_TOKEN = 'うんたらかんたら(取得したやつ)'
    ACCESS_TOKEN_SECRET = 'うんたらかんたら(取得したやつ)'
    auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
    return auth

auth = get_oauth()
api = tweepy.API(auth,timeout=180)
```
うんたらかんたらと書かれているところは上記のTwitterAPIKeyで取得したものを入力してください。

```python:syutoku.py
olddic=[]
with open('tweets.txt', 'r',newline='',encoding='utf-8') as g:
    line = g.readline() # 1行を文字列として読み込む(改行文字も含まれる)
    while line:
        olddic.append([line.replace('\n','').replace('\'','&sing').replace('\"','&quot').replace(',','&cam')])
        line = g.readline()
old_id=olddic[0]
olddic.pop(0)
#ツイート取得
tweet_data = olddic
count=0
for tweet in tweepy.Cursor(api.user_timeline,screen_name = "データを集めたい人のアカウント名(@は不要)",exclude_replies = True,include_rts= False).items():
    if count==0:
        new_id=tweet.id
    if int(tweet.id)>int("".join(old_id)):
        tweet_data.append([tweet.text.replace('\n','').replace('\'','&sing').replace('\"','&quot').replace(',','&cam')])
    count+=1
#txt出力
with open('tweets.txt', 'w',newline='',encoding='utf-8') as f:
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow([new_id])
    writer.writerows(tweet_data)
pass
```
これでtweets.txtにRTとリプライを除外したツイート本文が1行に1ツイート出てくるはずです。
引用符とカンマはエスケープしなかったら徐々にテキストファイル内で増殖して大変なことになったのでエスケープさせました。
残念なことにAPIにより取得できるツイートは１週間前までのものに限られるそうです。なので丸ごとweets.txtを更新すると古いツイートから消えてしまうので、tweets.txtの先頭に取得した最新のツイートのIDを用意して、新しいツイートだけを追加していくことにしました。最初このファイルを実行する際tweets.txtは空ではなく「0」とだけ入力しておくとうまくいくと思います。
これで既存のツイートだけから文章をつなぎ合わせてツイートする人工無能から、**アカウントの主がTwitterを更新すれば**それに合わせて生成するツイートも学習されていく人工知能に進化しました。~~やったね!~~


# ランダム文の生成
次にランダム文を生成してツイートするプログラムmain.pyを作ります。

```python:main.py
import tweepy
from janome.tokenizer import Tokenizer
import csv
from nltk.util import ngrams
from nltk.lm import Vocabulary
from nltk.lm.models import MLE

CONSUMER_KEY = 'うんたらかんたら(取得したやつ)'
CONSUMER_SECRET = 'うんたらかんたら(取得したやつ)'
ACCESS_TOKEN = 'うんたらかんたら(取得したやつ)'
ACCESS_TOKEN_SECRET = 'うんたらかんたら(取得したやつ)'
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)

file = 'wakachi.txt'
t = Tokenizer()

with open('tweets.txt', 'r', encoding='utf-8') as line:    
    f = open(file, 'w')
    writer = csv.writer(f, lineterminator='')
    next(line)
    for k in line:
        s=[]
        for token in t.tokenize(k.replace('&sing','\'').replace('&quot','\"').replace('&cam',',')):
            if s==[]:
                s=token.surface
            else:
                s=s+' '+token.surface
        ss=''.join(s)
        ss=ss+"\n"
        ss=ss.replace('\"\"\"\"\"','').strip(" ")
        f.write(ss) # 引数の文字列をファイルに書き込む
f.close() # ファイルを閉じる        
```
まずはjanomeを用いてtweets.txtを形態素解析して分かち書きします。確認用としてwakachi.txtに出力しておきます。

```python:main.py
words = [('<BOP> ' + l + ' <EOP>').split() for l in open(file, 'r', encoding='utf-8').readlines()]
vocab = Vocabulary([item for sublist in words for item in　sublist])
text_bigrams = [ngrams(word, 2) for word in words]
text_trigrams = [ngrams(word, 3) for word in words]
lm2 = MLE(order = 2, vocabulary = vocab) 
lm3 = MLE(order = 3, vocabulary = vocab)
lm2.fit(text_bigrams) 
lm3.fit(text_trigrams)
```
分かち書きした文章の先頭に< BOP >,終わりに< EOP >を取り付けた上で二語、三語の並びであるbi-gram,tri-gramのリストを作ります。ここでMLEによって言語モデルを準備し、bi-gram,tri-gramで言語モデルを学習させます。

```python:main.py
context = ['<BOP>']
w= lm2.generate(text_seed=context)
context.append(w)
for i in range(0, 100):
    w = lm3.generate(text_seed=context)
    context.append(w)
    if '<EOP>' == w:
        context.remove('<BOP>')
        context.remove('<EOP>')
        res=''.join(context)
        api.update_status(res)
        break
```
上記の言語モデルを利用します。まず< BOP >につながる語(先頭の語)をbi-gramから見つけて、以降は前の二語につながる語を連結させていきます。< EOP >に到達したところで終了してツイートとして出力させます。
#cronによる自動投稿
ぶっちゃけ[ここ](https://qiita.com/deadhead13/items/ef909ef69277ef11b345)に分かりやすく載ってるのでコマンドを貼ってまで説明するまでもないと思います。
基本的に必要な手順は
1. `PATH=/Users/ユーザー名/anaconda3/bin:/opt……/hg/bin`を貼ってPython3で実行させる
2. 上記で作成したsyutoku.pyを定期的に実行させるコマンドをvimで打ち込む
3. 上記で作成したmain.pyを定期的に実行させるコマンドをvimで打ち込む
といった感じです。
# 参考文献
[Tweepyでツイートを取得](https://qiita.com/i_am_miko/items/a2e5168e619ed37afeb9)
[Tweepyで自動フォロー＆ファボを実装し、Cronで定期実行](https://qiita.com/deadhead13/items/ef909ef69277ef11b345)
[Twitter API Key を取得する方法 - phiary](http://phiary.me/twitter-api-key-get-how-to/#post-h2-id-0)
[Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ　※2019年8月時点の情報](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)