形態素解析からのマルコフ連鎖、PythonでTwitterのbotベースを作ってみよう

2015.5.16

a0782_000805Python関係の記事も増えてきたところで、Twitterのボット的なものを作ってみましょう。今回は圧縮新聞や、しゅうまい、のような一見カオスな文章をつぶやくbotの基礎を構築してみましょう。そのまま使用しても割と面白い結果が得られますのでチャレンジしてみましょう。

Twitter botの制作

Twitter Developerへの登録

以前も登録の仕方は書きましたがおさらいしておきましょう。

https://dev.twitter.com/

上記URLに飛んでTwitterのアカウントでログインします。

twitdevManage Your Appsをクリックして今回製作するbotを登録してAPIキーを取得します。

入力画面

Name:アプリケーションの名前を考えて入れましょう。
Description:アプリケーションの説明です。
Website:ブログやWebサイトのURLを入力します。

Consumer Key (API Key)、Consumer Secret (API Secret)、Access Token、Access Token Secretを取得すればOKです。この4つのキーは絶対に外部に漏らしてはいけません。アクセスレベルがRead, write, and direct messagesになっていることをちゃんと確認しておきましょう。

仕組みを理解しよう

今回はTwitter APIを使用してツイートするわけなので、https://api.twitter.com/1.1/statuses/update.jsonを使用します。Twitter APIに関してはどんなものがあるのか公式サイト https://dev.twitter.com/rest/publicから確認しておきましょう。今後色々なアイデアが出てくるかもしれませんよ。

botの仕組みは以下のようにしていきます。

  1. Twitterでキーワードを検索し、キーワードが含まれるツイートを保存
  2. 保存したツイートから@やRT、リンクを除去
  3. 上記を除去したテキストファイルを読み込み
  4. 読み込んだテキストを形態素解析
  5. 分かち書きしたワードをマルコフ連鎖で文章に変換
  6. ツイートする

という流れとなります。

必要なモジュールをインストールしよう

ターミナルを起動して必要なモジュールをインストールしていきましょう。まずは認証周りです。

$ pip install requests requests_oauthlib

pipがインストールされていない場合はまずはpip からインストールしておいてください。pipのインストールの仕方は以下。

$ easy_install pip

次に形態素解析を行うMeCabをインストールしましょう。http://mecab.googlecode.com/svn/trunk/mecab/doc/index.html#download
上記URLからダウンロードしてインストールします。細かいインストール方法をいかに記述しておきますので頑張りましょう。

brewが既にインストールされていればbrewをupdateしてbrewからインストールもできます。その場合は以下。

$ brew install mecab

そして辞書のインストール。

$ brew install mecab-ipadic

他の方法は以下を参考。

$ brew install wget
$ wget https://mecab.googlecode.com/files/mecab-python-0.996.tar.gz
$ pip install mecab-python-0.996.tar.gz 

これでモジュール周りのインストールは終了となります。

コードを書いていこう

検索用の関数を書いていこう

ではいよいよコードを書いていきましょう。

#!/user/bin/env python
# -*- coding: utf-8 -*-
from requests_oauthlib import OAuth1Session
import json
import sys
import MeCab
import random
import re

まずはお約束の2行を記述し、その後に使用するモジュールをインポートします。OAuth1SessionはTwitterの認証周りですね。jsonはTwitterから戻ってくるデータの形式がjsonなのでそのために使用します。sysは文字周りだと考えておけばいいでしょう。MeCabは形態素解析に必要となってきますね。randomモジュールはマルコフ連鎖でテキストを生成するために使用します。reは正規表現のために使用するモジュールとなります。

C_KEY = "*************************************"
C_SECRET = "*************************************"
A_KEY = "*************************************"
A_SECRET = "*************************************"

次にTwitter APIでのキーを記述していきます。本来安全性のために別ファイルに分けるべきなのですが今回はスクリプトの中に組み込んでしまいましょう。Consumer Key (API Key)、Consumer Secret (API Secret)、Access Token、Access Token Secretを順に入れ込んでいきます。

ツイートするためのテキスト収集にhome_timelineを取得してもいいのですが、それではフォローしている人しか拾えません。せっかくなので特定のキーワードだけを拾うように設計してみましょう。

def Search_words():
    url = "https://api.twitter.com/1.1/search/tweets.json?"
    params = {
                "q": unicode(search_words, "utf-8"),
                "lang": "ja",
                "result_type": "recent",
                "count": "100"
                }
    tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
    req = tw.get(url, params = params)
    tweets = json.loads(req.text)
    for tweet in tweets["statuses"]:
        f = open("tweet.txt" , "aw")
        lists = (tweet["text"].encode("utf-8"))
        if "http" in lists:
            lists = lists.split("http", 1)[0]
            lists = lists.split("@")[0]
            lists = lists.split("RT")[0]

            f.write(lists)
            f.flush()
            f.close()

Search_wordsという関数を作ってみました。ここではTwitter APIの検索用APIを使用します。ぱっとみて大体わかるでしょうか。保存するテキストにはツイート本文しか必要ないので(tweet[“text”].encode(“utf-8”))としています。この本文にもhttpや@、RTが含まれるので.splitを使用し切り取っていきます。

このスクリプトの同一フォルダ内にtweet.txtという名前で保存をかけます。tweet.txtというファイルが無ければ自動生成、あれば上書きをしていくという仕組みになっています。当然ファイルを開いでいるのでf.flush()でキレイにしてからf.close()で閉じておきます。

最終的にWhileのループでぐるぐる回したいので以下のようになります。

#!/user/bin/env python
# -*- coding: utf-8 -*-
from requests_oauthlib import OAuth1Session
import json
import sys
import MeCab
import random
import re

while True:
    search_words = raw_input(u"words: ")

    C_KEY = "*************************************"
    C_SECRET = "*************************************"
    A_KEY = "*************************************"
    A_SECRET = "*************************************"

    def Search_words():
        url = "https://api.twitter.com/1.1/search/tweets.json?"
        params = {
                "q": unicode(search_words, "utf-8"),
                "lang": "ja",
                "result_type": "recent",
                "count": "100"
                }
        tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
        req = tw.get(url, params = params)
        tweets = json.loads(req.text)
        for tweet in tweets["statuses"]:
            f = open("tweet.txt" , "aw")
            lists = (tweet["text"].encode("utf-8"))
            if "http" in lists:
                lists = lists.split("http", 1)[0]
                lists = lists.split("@")[0]
                lists = lists.split("RT")[0]

                f.write(lists)
                f.flush()
                f.close()

    if search_words:
        Search_words()
        Mecab_file()
    else:
        break

Mecabで形態素解析をしてマルコフ連鎖でテキスト生成

さてここからが本題です。先ほど保存したテキストファイルをMeCabに投げて、マルコフ連鎖でテキスト生成をしなければなりません。では一気に行きます。

def Mecab_file():   
    f = open("tweet.txt","rb")
    data = f.read()
    f.close()

    mt = MeCab.Tagger("-Owakati")

    wordlist = mt.parse(data)
    wordlist = wordlist.rstrip(" \n").split(" ")

    markov = {}
    w = ""

    for x in wordlist:
        if w:
            if markov.has_key(w):
                new_list = markov[w]
            else:
                new_list =[]

            new_list.append(x)
            markov[w] = new_list
        w = x

    choice_words = wordlist[0]
    sentence = ""
    count = 0

    while count < 90:
        sentence += choice_words
        choice_words = random.choice(markov[choice_words])
        count += 1

        sentence = sentence.split(" ", 1)[0]
        p = re.compile("[!-/:-@[-`{-~]")
        sus = p.sub("", sentence)

    words = re.sub(re.compile("[!-~]"),"",sus)
    twits = words + " 【tweet from twmarkov】"

MeCabで保存したテキストを展開し分かち書きを行います。マルコフ連鎖のためのテーブルを作成し、randomでキーワードをチョイスしリストへほり込んでいきます。英数字の文字列を正規表現で取り除いてツイートするためのテキストを生成し、投稿準備完了。という流れですね。ちょっと説明が簡素すぎますがまぁいいでしょう。

これをTwitterへ投稿しないといけないのでPOST用のスクリプトをくっつけます。

url = "https://api.twitter.com/1.1/statuses/update.json"
params = {"status": twits,"lang": "ja"}
tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
req = tw.post(url, params = params)
if req.status_code == 200:
       print "Success! Your Tweet"
else:
       print req.status_code

params = {“status”: twits,”lang”: “ja”}で、先ほど生成したtwits = words + ” 【tweet from twmarkov】”をつぶやくわけです。

フルのコード

では全部を合体させたコードが以下になります。これをターミナルで実行して任意のキーワードを検索しPOSTすることが可能になりました。

#!/user/bin/env python
# -*- coding: utf-8 -*-
from requests_oauthlib import OAuth1Session
import json
import sys
import MeCab
import random
import re

while True:
    search_words = raw_input(u"words: ")

    C_KEY = "*************************************"
    C_SECRET = "*************************************"
    A_KEY = "*************************************"
    A_SECRET = "*************************************"

    def Search_words():
        url = "https://api.twitter.com/1.1/search/tweets.json?"
        params = {
                "q": unicode(search_words, "utf-8"),
                "lang": "ja",
                "result_type": "recent",
                "count": "100"
                }
        tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
        req = tw.get(url, params = params)
        tweets = json.loads(req.text)
        for tweet in tweets["statuses"]:
            f = open("tweet.txt" , "aw")
            lists = (tweet["text"].encode("utf-8"))
            if "http" in lists:
                lists = lists.split("http", 1)[0]
                lists = lists.split("@")[0]
                lists = lists.split("RT")[0]

                f.write(lists)
                f.flush()
                f.close()


    def Mecab_file():   
        f = open("tweet.txt","rb")
        data = f.read()
        f.close()

        mt = MeCab.Tagger("-Owakati")

        wordlist = mt.parse(data)
        wordlist = wordlist.rstrip(" \n").split(" ")

        markov = {}
        w = ""

        for x in wordlist:
            if w:
                if markov.has_key(w):
                    new_list = markov[w]
                else:
                    new_list =[]

                new_list.append(x)
                markov[w] = new_list
            w = x

        choice_words = wordlist[0]
        sentence = ""
        count = 0

        while count < 90:
            sentence += choice_words
            choice_words = random.choice(markov[choice_words])
            count += 1

            sentence = sentence.split(" ", 1)[0]
            p = re.compile("[!-/:-@[-`{-~]")
            sus = p.sub("", sentence)

        words = re.sub(re.compile("[!-~]"),"",sus)
        twits = words + " 【tweet from twmarkov】"

        url = "https://api.twitter.com/1.1/statuses/update.json"
        params = {"status": twits,"lang": "ja"}
        tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
        req = tw.post(url, params = params)
        if req.status_code == 200:
            print "Success! Your Tweet"
        else:
            print req.status_code


    if search_words:
        Search_words()
        Mecab_file()
    else:
        break

検索文字列を入れ続ける限りはループで回り続ける構造です。while count < 90: のカウント数を変更すれば文字数の調整が可能です。最終的にどれくらいの長さのツイートをするかこの辺りを調整しても面白いと思います。検索したキーワードから保存したツイートの量がある一定を超えたあたりから面白いカオスなツイートが出てくるはずです。 実にシンプルなマルコフテーブルなのでこの辺りをしっかり手を入れてやればしゅうまい、圧縮新聞などのbotが作れるはずです。 後はこのスクリプト自体をcronで叩きに行くとか、そもそも検索せずにストリーミングAPIを使うとか様々な展開が考えられると思います。色々改造して遊んでみましょう。