勉強も兼ねて、お見合いbotを作ってみた。
@kin7008 はじまったな。 RT @aohasu: お見合いBot…ゴクリ QT @sinsoku_listy: 両方の単語を収集するbotがいたら・・・ RT @aohasu: 彼女欲しいトークもむなしい… QT @miloooks: 彼氏欲しいトークがむなしい…
もっと早く作ろうと思ってたけど、なかなか私生活の方が忙しかったり、
他の事をやってたりで遅くなってしまいましたorz
せっかく作った訳ですし、ブログに書いておきます。
お見合いbot
お見合いbot(@omiai_bot)
機能
- 「彼女ほしい」「彼氏ほしい」を検索します。
- "@user" を "_user"に置換してから非公式RT*1します。
- http://が含まれていると非公式RTしません。
- 同意する内容がRT/QT前にないと非公式RTしません。
勉強用ですので、機能はショボいです(´・ω・)ショボーン
他のbot作ってる人はみんな凄い(´・ω・)ッス
ソース
テストコードも書いてませんし、やっつけ仕事になってます・・・
ソース全部を説明するのは面倒なので、簡単に気になった所だけ説明を書いておきます。
ちなみに、ライブラリはtweepyを使用しました。
omiaibot.py
#!/usr/bin/env python # -*- coding: utf-8 -*- from google.appengine.ext import webapp from google.appengine.ext.webapp import util from google.appengine.ext import db import re import tweepy class Status(db.Model): id = db.IntegerProperty() user = db.StringProperty() text = db.TextProperty() class OmiaiBot(webapp.RequestHandler): def get(self): self.api = self.authTwitter() searchwords=[u'彼女ほしい OR 彼氏ほしい'] tweets = self.search(searchwords) tweets = self.filter(tweets) self.response.headers['Content-Type'] = 'text/html' for tweet in tweets: self.response.out.write('RT @' + tweet.from_user + ': ' + tweet.text + '<p>') self.post(tweet) self.put(tweet) def search(self, words): ''' wordsの語句を一つずつ検索し、検索結果を一つのリストにして返す。 ''' resultlist = [] for word in words: resultlist += self.api.search(word.encode('utf-8')) # 重複要素を消去したリストを返す return sorted(set(resultlist), key=resultlist.index) def _isPosted(self, status): ''' DBにstatusのidが保存されていたらTrue, 保存されていなければFalseを返す。 ''' query = Status.all() query.filter('id =', status.id) return query.count() > 0 def _isAgree(self, text): ''' text内の「彼女ほしい」に同意していればTrue, 同意していなければFalseを返す。 ''' doui = re.search(u'(ほしい|欲しい)', text) rtqt = re.search('(RT|QT)', text) return doui != None and rtqt != None and doui.span() < rtqt.span() def filter(self, status): ''' statusから条件に合わないステータスを消去する。 * 前回までのcronで既にpostしているステータス * リンク(http)がついてるステータス * 同意するpostがRT/QTの前にないステータス ''' filterdStatus = [] for s in status: if (not self._isPosted(s)) and (not re.search('http://', s.text)) and self._isAgree(s.text): filterdStatus.append(s) return filterdStatus def post(self, status): ''' @userを_userに置換し、リプライが飛ばないように修正してからpostする。また、160字を超えていた場合はpostしない。 ''' text = 'RT @' + status.from_user + ': ' + status.text cnv_status = re.sub('@', '_', text) if len(cnv_status) < 160: self.api.update_status(cnv_status) def put(self, status): ''' postするstatusをデータストアに保存する ''' s = Status() s.id = status.id s.user = status.from_user s.text = status.text s.put() def authTwitter(self): ''' Twitterの認証を行い、tweepy.APIを返す。 ''' ckey = '' csec = '' akey = '' asec = '' auth = tweepy.OAuthHandler(ckey, csec) auth.set_access_token(akey, asec) return tweepy.API(auth) if __name__ == '__main__': import doctest doctest.testmod()
まず、処理の流れですが、15行目の"def get(self):"から始まります。
普通のプログラムのint main()みたいなものです。
というわけで、16行目から順に説明します。
OAuth認証
16 self.api = self.authTwitter() ~~ 83 def authTwitter(self): 84 ''' Twitterの認証を行い、tweepy.APIを返す。 85 ''' 86 ckey = '' 87 csec = '' 88 akey = '' 89 asec = '' 90 auth = tweepy.OAuthHandler(ckey, csec) 91 auth.set_access_token(akey, asec) 92 93 return tweepy.API(auth)
ここでtweepy使ってOAuth認証してます。
twitterAPI用pythonライブラリtweepyを使えるようになるまで。 - Number6の「あーあ、俺に狐の嫁さんできねぇかなぁ!!」のエントリが分かりやすいです。
twitterから検索
18 searchwords=[u'彼女ほしい OR 彼氏ほしい'] 19 tweets = self.search(searchwords) ~~ 28 def search(self, words): 29 ''' wordsの語句を一つずつ検索し、検索結果を一つのリストにして返す。 30 ''' 31 resultlist = [] 32 for word in words: 33 resultlist += self.api.search(word.encode('utf-8')) 34 35 # 重複要素を消去したリストを返す 36 return sorted(set(resultlist), key=resultlist.index)
ここではリストで渡した単語を順に検索し、検索結果を返しています。*2
フィルタリング
20 tweets = self.filter(tweets)
非公式RTの対象でないつぶやきを除去します。
45 def _isAgree(self, text): 46 ''' text内の「彼女ほしい」に同意していればTrue, 同意していなければFalseを返す。 47 ''' 48 doui = re.search(u'(ほしい|欲しい)', text) 49 rtqt = re.search('(RT|QT)', text) 50 return doui != None and rtqt != None and doui.span() < rtqt.span() ~~
RT/QT前に"ほしい"、"欲しい"があるかをチェックしています。
「@hoge 欲しい RT: @piyo 彼女ほしい」とかならTrueになります。
今回、初めてタプルの比較を知りました。
twitterに非公式RT
66 def post(self, status): 67 ''' @userを_userに置換し、リプライが飛ばないように修正してからpostする。また、160字を超えていた場合はpostしない。 68 ''' 69 text = 'RT @' + status.from_user + ': ' + status.text 70 cnv_status = re.sub('@', '_', text) 71 if len(cnv_status) < 160: 72 self.api.update_status(cnv_status)
非公式RTをするためには、69行目のようにpostする文字列を作る必要があります。
また、160文字を超える可能性もあるので、文字数チェックも必要です。
今回は上記2点に加え、@を_に置換する処理も加えています。
GitHub
参考になるか分からないですが、ソースをGitHubに上げておきます。
sinsoku's twitterbot at master - GitHub