理系頭巾の知見

なんちゃって理系の頭巾が知ったことや考えたことをまとめているブログです。

理系頭巾の知見

Pythonでみんなの呟きを解析して自動でツイートするTwitter Botを作った話

はじめに

f:id:gqp:20180421122550p:plain

なんだこいつ!?

研究のやり過ぎで頭がやられちまったのか? まともな文章になってねぇ。

でもしょうがないね、これBotだから。

今回はPythonの練習がてら僕が作ったTwitter Bot「理系の大学院生ちゃん」のお話。

twitter.com

まだ「みんなのPython」を途中までしか読んでない状態で作ったので、何卒お手柔らかにお願いします。

↑分かりやすくて良い本だけどサンプルコードのガルパン推しがすごい

大学院生ちゃんのソースコード

前書きはそこそこに早速コード載せちゃいます。ドン!

import twitter
import shutil
import random
import MeCab
import re
import signal
import sys
from pyfiglet import Figlet

#ターミナルの横サイズ会得+区切り線
terminal_size=shutil.get_terminal_size()
line="-"*terminal_size[1]
#ツイート数とファボ数用変数
t_count=0
f_count=0
#品詞:テキストファイル名
h_d={"名詞":"meishi","副詞":"hukushi","形容詞":"keiyou","動詞":"doushi","記号":"kigou","感動詞":"kandou","助詞":"jyoshi","接続詞":"setsuzoku","助動詞":"jodoshi"}
#MeCab
m=MeCab.Tagger()
#バグ回避
m.parse("")
#巨大文字の形式指定
moji=Figlet(font="roman")
#認証用文字列
a_d={"ck":"****","cs":"****","at":"****","ts":"****"}
auth = twitter.OAuth(consumer_key=a_d["ck"],consumer_secret=a_d["cs"],token=a_d["at"],token_secret=a_d["ts"])
#認証
t = twitter.Twitter(auth=auth)
#Userstreamを用いる
t_userstream = twitter.TwitterStream(auth=auth,domain='userstream.twitter.com')

#ツイート表示
def view_t():
    user=msg['user']['name']
    global tweet
    global t_count
    global tweet_id
    tweet=msg['text']
    tweet=re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", tweet)
    tweet_id=msg['id']
    t_count+=1
    print(line)
    print(t_count,":",user,":",tweet)
#品詞判定、単語ファイル書き込み
def kakikomi():
    h_rom=h_d[hinshi]
    f=open(h_rom+".txt","a")
    f.write(word+"\n")
    f.close
#各品詞から単語を一つ抽出
def select(hinshi):
    h_rom=h_d[hinshi]
    f=open(h_rom+".txt","r")
    f=f.readlines()
    f=random.sample(f,1)
    f=f[0]
    f=re.sub("\n","",f)
    return f
#ツイート数%10==0の時のアナウンス
def anno():
    announce_t=str(t_count)+"\nTweet"
    announce_f=str(f_count)+"Like"
    announce=moji.renderText(announce_t)
    announce2=moji.renderText(announce_f)
    print(line,"\n",announce,"\n",announce2)
#自動いいね
def like():
    if "理系" in tweet or "休講" in tweet and "RT" not in tweet and "@" not in tweet:
        t_id=int(tweet_id)
        global f_count
        f_count+=1
        print(line)
        t.favorites.create(_id=t_id,include_entitles="true")
        announce_f=str(f_count)+"Like"
        announce=moji.renderText(announce_f)
        print(announce)
#ツイート
def auto_tweet():
    t.statuses.update(status=t_text)
#リプライ
def reply():
    if "@rikei_daigakuin" in tweet and "RT" not in tweet:
        t.statuses.update(status="@"+msg["user"]["screen_name"]+" "+t_text,in_reply_to_status_id=tweet_id)
    elif "大学院" in tweet:
        t.statuses.update(status="@"+msg["user"]["screen_name"]+" "+"やだぁ!",in_reply_to_status_id=tweet_id)
#フォローチェック
def check_follow():
    follower=t.followers.ids(user_id="rikei_daigakuin")
    friends=t.friends.ids(user_id="rikei_daigakuin")
    yet_follow = set(follower["ids"]) - set(friends["ids"])
    list_follow = list(yet_follow)
    if len(list_follow)!=0:
        for user in list_follow:
            t.friendships.create(user_id=user)
            print(line,"\n",user,"さんをフォローしたよ!")
#終了時用
def handler(signal, frame):
        t.statuses.update(status="寝るよー"+"."*random.randint(1,10)+"--bot機能停止中--")
        sys.exit(0)
#何か入力されたらhandlerが起動       
signal.signal(signal.SIGINT, handler)
#実行されるのはここから。起きたツイート→ループ開始。
t.statuses.update(status="起きました"+"!"*random.randint(1,10)+"--bot機能稼働開始--")
for msg in t_userstream.user():
    if 'text' in msg:
        global t_text
        t_text=(select("形容詞")+select("副詞")+select("動詞")+select("副詞"))#select("記号")+select("名詞")+select("形容詞")+select("記号"))
        view_t()
        like()
        reply()
        #ツイートを分解して各ファイルに書き込み
        mc=m.parseToNode(tweet)
        while mc:
            word=mc.surface
            hinshi = mc.feature.split(",")[0]
            if hinshi!="BOS/EOS" and hinshi in h_d:
                kakikomi()
            mc=mc.next
        print(line)
        print(t_text)
        #ツイート数が十の倍数のときアナウンス&ツイート
        if t_count%10==0:
            anno()
        if t_count%20==0:
            auto_tweet()
            #未フォローチェック
            check_follow()
モジュールを色々試しに入れてみたらコードが100行超えてしまいました。

スポンサーリンク

実際に動かすとどうなる?

TwitterBotというとサーバーで動かすのが普通(?)というか楽だと思うんですが、何しろ当方プログラミング初心者なのでそこらへんの知識がありません。

なのでいつもこの大学院生ちゃんのプログラムはターミナルから動かしています。

なにか作業をしながら、流れてくるツイートだったり院生ちゃんの自動ツイートを見たりしています。

f:id:gqp:20180421113550p:plain

上から順に説明していくと、でっかく「10Tweet 0Like」ってなってるのが「読み込んだツイート数とお気に入り登録した数」です。

基本的にこのプログラムは起動しっぱなしにしているので、ふと見た時にそういう数字が分かればいいな、と思って追加した仕様です。

ちなみ文字をクソデカで表示するモジュールはpyfigletを使っています。面白いモジュールが沢山ありますよね、python。

qiita.com

そして下がフォローしているユーザーのつぶやきです。

「読み込んだツイート順番」:「ユーザー名」:「つぶやき内容」ですね。

ここらへんはTweetDeckの完全劣化です。笑

ただアイコンや画像が出てこないので、なんとなく「誰かがツイートしてる雰囲気がほしいだけ」って時には向いてます。

Deck見てるとぼけーっとして気がついたらかなり時間が経ってることってありますし。

自動フォロー返し&ツイート

f:id:gqp:20180421114636p:plain (変数の受け渡しが雑なのでユーザー名ではなくID(数字)のままになってる)

大学院生ちゃんは20ツイート読み込み毎に一回、フォローされているユーザーのチェックを行います。

フォローしているユーザー・されているユーザーの間で、所謂「片思い状態」になってしまっている人にフォローを返します(自動フォロー返し)。

本当はフォローされたらすぐフォロバ、というのをやりたかったんですがそれだと逐次フォロワーを確認する必要があり、すぐにTwitter APIの限界に達してしまうんですよね。

なのでツイートを20読み込んだときのみフォロバをするようにしました。

本当はここらへんも上手くやれば即時フォロバもできそうなんですが...。

あと院生ちゃんのクソメイン機能に自動ツイートがあります。

上の画像一番下、「怖いどうできどう」って言ってますよね。

これはMeCabを用いた形態素解析でTLのつぶやきを品詞分解し、各テキストファイルに記録したものからランダムに作られた文章です。

最初は「名詞」+「副詞」+「動詞」みたいな感じで比較的普通の文章になるようにしていたんですが、MeCabが英語や数字も名詞と判断することがありとりあえず名詞は省きました。

省いた後に「形容詞」+「副詞」+「動詞」+「副詞」とかいうクソ雑文を暫定的に採用してしまったので今も院生ちゃんはまともな日本語を喋れていません。

今はテキストファイルの中からローマ字や数字を削除して日本語のみにするプログラムを作っているんですが、誤って中身を全部消してしまったりとなかなか難しいですね。

文章の作り方もパターンを増やしていきたいところ。

会話もできるよ!

院生ちゃんbotはリプライを通して会話をすることが可能です。

といっても院生ちゃんはまともな日本語をまだ組み立てられる状態ではないので会話になりませんが...。

f:id:gqp:20180421120210p:plain

ちなみに返信速度はめちゃ早いです。ツイート送信したらほぼ同時くらいに返信されます。

暇で暇で死にそう!って時は院生ちゃんにちょっかいかけてみて下さい。

自動いいね!するよ!

院生ちゃんは「理系」と「休講」がツイートに含まれていると即いいねします。めちゃ早。

ちなみにRTされたツイートや誰かへのリプライには反応しません。

いいねをする対象はもっと増やしたいですね。

ちなみに「大学院」という文字列がツイートに含まれている場合は「やだぁ!」とリプライを送ります。

f:id:gqp:20180421120930p:plain

プログラム開始時・終了時の挙動

一応プログラム起動時と終了時に何かツイートした方がいいかな?と思ったので

こんな感じにツイートするようにしました。

同じ内容のツイートは一日に何回も送れないので、「!」の数を乱数で変更させています。

プログラムを「Control+C」で終了させるときにもツイートさせるようにしたかったので、signalというモジュールを使っています。

pythonの練習がてら気軽に作り始めたbotでしたが、勉強になることが多かったです。

モジュールのおかげで難しいことを考えずに済むんですが、やっぱり文字列の扱いとか基本的なことが本を読んだだけでは理解しきれていないことがわかります。

これからもpythonの勉強は何か実際に作りながらできたらな、と思います。

おわりに

Twitterのbot作成はググれば無限にブログ記事やQiitaが出てくるのでほんと初心者にうってつけです。

でもtweepyというモジュールがpython3系では使用できないという点には注意が必要です。

結構検索して出て来る記事にはtweepyを使ったものが多いので。

それでは今回はこのへんで!さよ〜なら〜