2017年07月21日

Twitter上のインデックス投資家はズボラなのか?(Python+Tweepyコード編)

と言うわけで、結果編からの続きです。
最近「Python+Tweepy」でTwitterのAPIを叩いているのですが、...
参考にしている書籍は「Pythonによるスクレイピング&機械学習 開発テクニック」になります。


TwitterのAPIを叩いて、特定の検索ワードのTweetを取得し、そのtweetを呟いているIDのアイコンを確認しています。defaultアイコンかどうかであれば単純な一致で良いと思いますが、せっかくなので上記書籍の七章のAverage Hashとハミング距離を用いています。

自己紹介文の文字数は、そのまま情報を取得して文字数を数えています。

# coding='utf-8'
###########################################################################
# Twitterの検索を行って、その検索に引っかかるユーザーのiconをチェックする
# defaultアイコンの人はズボラと判断する
# アカウント情報が必要になるのでforegoで.envを読みだす
# Usage : .\forego.exe run python.exe .\tweepy_search.py <検索ワード> <検索数> <Debugファイル名>
# Example : .\forego.exe run python.exe .\tweepy_search.py "インデックス投資" 100 bbb.txt

from PIL import Image
import numpy as np
import os
import sys
import tweepy
import urllib.request
from requests_oauthlib import OAuth1Session
import matplotlib.pyplot as plt


# Agerage hash生成
def average_hash (fname, size = 16):
img = Image.open(fname)
img = img.convert('L')
img = img.resize((size,size), Image.ANTIALIAS)
pixel_data = img.getdata()
pixels = np.array(pixel_data)
pixels = pixels.reshape((size,size))
avg = pixels.mean()
diff = 1 * (pixels > avg)
return diff

# np2hashは今回は使用しない
def np2hash(ahash):
bhash = []
for n1 in ahash.tolist():
s1 = [str(i) for i in n1]
s2 = "".join(s1)
i = int(s2,2)
bhash.append("%04x" % i)
return "".join(bhash)

# 元画像と比較画像のハミング距離の計算
def hamming_dist(a,b):
alist = a.reshape(1,-1)
blist = b.reshape(1,-1)
dist = (alist != blist).sum()
return dist

# 環境変数から認証情報を取得する。
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_TOKEN_SECRET = os.environ['ACCESS_TOKEN_SECRET']

# 初期化
maxId = 999999999999999999 # 検索結果用ID
maxIdOld = 999999999999999999 # 検索結果用ID
words = {} # 単語とその登場回数を格納
searchPage = 0

# 認証情報を使う
# wait_on_rate_limit = True はAPI制限に引っかかったときにWaitする設定
auth = tweepy.OAuthHandler(CONSUMER_KEY,CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN,ACCESS_TOKEN_SECRET)
api = tweepy.API(auth, wait_on_rate_limit = True)

checkedUserList = []
totalNum = 0
zuboraNum = 0
totalBioLenSum = 0
totalBioLenList = []
searchWord = sys.argv[1] # 検索WORD
maxPage = int(sys.argv[2]) # maxPage * 100のTweetを取得する
outFileName = sys.argv[3] # Debug出力

# 出力ファイルOpen
outFile = open(outFileName,'w',encoding='utf-8')

# defaultアイコンのhash
srcHash = average_hash(os.path.join(os.getcwd(),"icon","default","default.jpg"))
while True:
# maxIdは前のid-1を渡すことに注意
searchTweets = api.search(q=searchWord,count=100,result_type='recent',max_id=maxId-1)
for tweet in searchTweets:
# Debug用にTweetをTextに保存しておく
# cmdline = str(tweet.user.screen_name) + str(tweet.text) + str(tweet.created_at) + str(tweet.id) +'\n'
# outFile.write(cmdline)

# 初めてチェックするUserのみを処理する
if not tweet.user.id_str in checkedUserList:
# API制限に引っかかったときの例外処理対策(実際はErrorは発生せずにWaitがかかる設定にしている)
try:
checkedUserList.append(tweet.user.id_str)
# Iconの取得
iconUrl = tweet.user.profile_image_url
iconPath = os.path.join("icon","get",tweet.user.id_str+"-"+tweet.user.screen_name+".jpg")
urllib.request.urlretrieve(iconUrl,iconPath)
# Hashの作成とdefaultアイコンとの比較
destHash = average_hash(os.path.join(os.getcwd(),iconPath))
hammingValue = hamming_dist(srcHash,destHash) / 256
# 自己紹介文の取得
bio = api.get_status(tweet.id).user.description
# 取得情報の処理
totalNum += 1
totalBioLenSum += len(bio)
totalBioLenList.append(len(bio))
print(hammingValue,":",tweet.user.screen_name)
# アイコンがdefaultアイコンだった時
if (hammingValue==0):
zuboraNum += 1
except:
print("[Error]:Because twitter API was accessed too much")
break

# idが今までのより小さければ代入
if(tweet.id < maxIdOld):
maxId = tweet.id
if maxIdOld == maxId:
# Tweetが見つからなければmaxIdOldと変わらないので検索結果をすべて受けとったと判断
break
elif searchPage == maxPage:
# もしくは最大で引数で指定したmaxPagex100までのTweetをチェックする
break
else:
maxIdOld = maxId
searchPage += 1

outFile.close()

# 結果出力
print("--------------- Result -----------------")
print("検索に引っかかったID数:",totalNum,"そのうちDefaultアイコンのID数:",zuboraNum,"Defaultアイコンの割合:",zuboraNum/totalNum*100,"%")
print("検索に引っかかったIDの自己紹介文の平均文字数:",totalBioLenSum/totalNum)

plt.hist(totalBioLenList,bins=20)
plt.title("自己紹介の文字数のヒストグラム")
plt.xlabel("自己紹介の文字数")
plt.ylabel("該当ID数")
plt.savefig("hist.png")
plt.show()
今回TwitterのAPIを叩いて気付いたのですが、15分間に情報取得のために叩けるAPIの回数制限って厳しいんですね。サードパーティのアプリを使っていてタイムラインが更新されないなーとか思っていたのですが、API回数制限によってタイムラインなど情報取得ができない状態が発生しているのに気づかされました。

スポンサーリンク
posted by くまなべ at 07:48 | Comment(0) | TrackBack(0) | Python
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/180404004
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック