プログラマ行進曲第二章

主にソフトウェア関連の技術をネタにした記事を執筆するためのブログ

Python製Twitter Bot"ぱい☆そん"(@pie_song)のソースコードを公開します!

先日、大学時代の友達の送別会に行ったんですが、その際、大学時代の友達から

「ブログ更新しろよ!」(※意訳)

と言われたので、久しぶりに更新しようと思い、記事を書いています。

ネタもさっぱり無い、というかサラサラっと記事に仕上げられるネタが無いなぁと困っていたところ、久しぶりにTwitter Bot"ぱい☆そん"のことを思い出し、ちょっと相手してみるかとソースコードを見ていたら以前詰まっていたエラーの原因がすんなりとわかったので修正→再稼働できたので、今回はTwitter Bot"ぱい☆そん"のネタで行きたいと思います。

と言っても技術的には碌にネタになるようなことも無い…というか僕が色々聞きたいことがあるくらいなので、

「じゃあ、このブログ上でソースコードを公開した上で『こんな機能をつけるにはどうしたらいいの?』といった自分の質問の答えを募集してみたらどうだろうか?」

という、何ともクレクレ厨*1な発想をネタにしようと閃きました!

そんなわけで、以下がTwitter Bot"ぱい☆そん"のソースコードです。
ID名やパスワードなど、個人情報に当たる箇所は一部変えております。

なお、ソースコード上でimportしている"twitter"という名前のモジュールは「python twitter tools」というライブラリです。このライブラリがあったからこそ、私みたいなヘボプログラマでもTwitter Bot(らしきもの)を作れたと言っても過言ではありません。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from twitter import *
import yahooapi.jlp as jlp
import random

__author__ = 'SHIMIZU Taku'
__version__ = '0.10'

# 'username', 'password'はそれぞれ、あなたがBot用に確保しているTwitterアカウントのIDとパスワードを入れること。
username = 'username'
password = 'password'

class TwitterBot(Twitter):
    def __init__(self, email=None, password=None):
        # スーパークラスの初期化メソッド呼び出し
        Twitter.__init__(self, email, password)
        
        # ここからAPI関連の変数。
        # TwitterAPIのリクエスト制限があるため、変数として値を保持している。
        # 他の上手い解決策があったらそれを採用すること。
        
        # タイムライン関連のAPI
        self.public_timeline = self.statuses.public_timeline()
        self.friends_timeline = self.statuses.friends_timeline(
                                    id="takuan_osho", count=20)
        self.timeline = self.statuses.user_timeline(count=200)
        self.replies = self.statuses.replies(count=200)
        self.mentions = self.statuses.mentions(count=200)
        
        # ステータス関連のAPIは、必要なときになったら使うもののため、
        # 変数は不必要
        
        # ソーシャルグラフ関連のAPI
        # ユーザ情報関連のAPIと名前が競合している関係で先に定義している。
        # 問題が他のところで発生するかもしれないので気をつけよ。
        self.friends_ids = self.friends.ids()
        self.followers_ids = self.followers.ids()
        
        # ユーザ情報関連のAPI
        self.friends = self.statuses.friends()
        self.followers = self.statuses.followers()
        
        # ダイレクトメッセージ関連のAPI
        # received_direct_messages = self.direct_message()
        # sent_direct_messages = self.direct_message.sent()
        
        # ポスト候補を記憶する変数
        self.wordslist = {'my_word_history': [], 'friends_word_history': []}
        self.my_word_history = []
        self.friends_word_history = []
        
        # 今までポストした自分の発言を一定数記憶する
        for i in range(200):
            self.wordslist['my_word_history'].append(
                self.timeline[i]['text'])
        
        # フォロワーの発言を一定数記憶する
        try:
            for id in self.followers_ids:
                for i in range(3):
                    self.friends_word_history.append(
                        self.statuses.user_timeline(id=id)[i]['text'])
        except:
            pass
        
def getRandomElement(elementslist):
    '''
    引数から要素をランダムに抽出する。
    
    引数 : 
        リスト・タプルを想定
    返り値 :
        文字列
    '''
    random.seed()
    random_element = elementslist[random.randint(0, (len(elementslist)-1))]
    return random_element
    
def makeStarryWord(word):
    '''
    引数に「☆」を挿入する
    
    引数 :
        文字列を想定
    返り値 :
        文字列
    '''
    center_line = len(word) / 2
    starry_word = word[:center_line] + '' + word[center_line:]
    return starry_word
    
def main():
   # try:
        # Twitterアカウントの情報取得
        bot = TwitterBot(username, password)
        
        # Yahoo! web API取得
        client = jlp.MAServiceAPI(
      "password") # "password"には、あなたのYahoo! APIのパスワードを入れること。
        
        print len(bot.friends_word_history)
        
        for i in range(15):
            # フォロワーの発言を表示して確認する
            try:
                print bot.friends_word_history[i]
                result = client.parse(sentence=bot.friends_word_history[i])
            except IndexError:
                print "IndexError occurred!"
            # 引っ張ってきたポストを"○○☆○○"に整形して記憶する
            for word in result.ma_result.word_list.word:
                try:
                    if word.pos=='名詞' and len(word.reading)==4:
                        starry_word = makeStarryWord(word.reading)
                        bot.wordslist['friends_word_history'].append(
                            starry_word)
                # 数値はreading属性を持たないため、エラーを返す
                except TypeError:
                    print "TypeError occurred! + %s" % word.reading
                # unicode関係のエラー。詳しくは不明。
                except AttributeError:
                    print "AttributeError occurred! + %s" % word
                    
        for id in range(3):
            bot.statuses.update(status=
                getRandomElement(bot.wordslist['my_word_history']))
            bot.statuses.update(status=
                getRandomElement(bot.wordslist['friends_word_history']))

if __name__ == '__main__':
    main()

どうだ! この適当なソースコード&コメントは!

…ぶっちゃけ、コメントも適当なことばっか書いているので、ソースコードと対応が取れているか不明です。

この後、このTwitter Botの「仕様」「目指していること」「実現したいがどうやったらいいのかわからないこと」などを書く予定でしたが、今は時間が無いので、後日追記したいと思います。

あ、勿論、ソースコードを見た上で、「ここ、こうしたほうがいいんじゃない?」とか「こういうことしたいんだったら、こうしたほうがいいよ」というハイパーエスパーな方がいらっしゃいましたら、是非コメントやメールでご教授お願いします!

*1:Google日本語入力の変換候補に出てきたから使っただけだよ!