さて、本日は「PythonでTwitterデータを取得しマップ上で表現する」です。つまり、ツイート情報から緯度経度を取得し、その緯度経度ごとにポイントを配置してみようと思います。手順は以下を参照してください。
各種必要なライブラリ等のインストール
必要な各種ライブラリをインストールしてください。
Twitter REST APIsを使うための認証ライブラリは必ずインストールしてください。
pip install requests_oauthlib
また、MongoDBもインストールが必要です。詳細は以下を参照してください。
PythonでMongoDBを弄ってみる - GIS奮闘記
これで準備完了です。まずはツイート情報を取得してみたいと思います。今回は「フットサル」という文言を含んだツイートを取得します。
ただ、ここでちょっと制限が。ツイート情報は1回のアクセスで100ツイートしか取得できず、アクセスも15分で180回までになったらしいです。
■GetTweet.py
from requests_oauthlib import OAuth1Session
import json, datetime, time, pytz, re, sys,traceback, pymongo
from pymongo import MongoClient
import numpy as np
KEYS = {
'consumer_key':'*****************',
'consumer_secret':'*****************',
'access_token':'*****************',
'access_secret':'*****************',
}
twitter = None
tweetdata = None
meta = None
def initialize():
global twitter, tweetdata, meta
twitter = OAuth1Session(KEYS['consumer_key'],KEYS['consumer_secret'],
KEYS['access_token'],KEYS['access_secret'])
connect = MongoClient('localhost', 27017)
db = connect.footsal
tweetdata = db.tweetdata
meta = db.metadata
def getData():
res = getTweetData(u'フットサル')
if res['result']==True:
if len(res['statuses'])==0:
sys.stdout.write("statuses is none. ")
elif 'next_results' in res['metadata']:
meta.insert({"metadata":res['metadata'], "insert_date": now_unix_time()})
for s in res['statuses']:
tweetdata.insert(s)
next_url = res['metadata']['next_results']
pattern = r".*max_id=([0-9]*)\&.*"
ite = re.finditer(pattern, next_url)
for i in ite:
mid = i.group(1)
break
def getTweetData(search_word):
url = 'https://api.twitter.com/1.1/search/tweets.json'
params = {'q': search_word,
'count':'100',
}
req = twitter.get(url, params = params)
if req.status_code == 200:
timeline = json.loads(req.text)
metadata = timeline['search_metadata']
statuses = timeline['statuses']
limit = req.headers['x-rate-limit-remaining'] if 'x-rate-limit-remaining' in req.headers else 0
reset = req.headers['x-rate-limit-reset'] if 'x-rate-limit-reset' in req.headers else 0
return {"result":True, "metadata":metadata, "statuses":statuses, "limit":limit, "reset_time":datetime.datetime.fromtimestamp(float(reset)), "reset_time_unix":reset}
else:
print ("Error: %d" % req.status_code)
return{"result":False, "status_code":req.status_code}
def now_unix_time():
return time.mktime(datetime.datetime.now().timetuple())
if __name__ == '__main__':
initialize()
count = 0
while(count < 180):
try:
count = count + 1
getData()
except:
print "Unexpected error:", sys.exc_info()[0]
break
こんな感じでツイート情報を取得しました。一回の実行ではデータが少ないので、何度か実行した方がいいかもしれません(←今さらながら自動化すればよかったと。。。)。
そして、ツイート情報自体に"coordinates"というフィールドが含まれており、GPSなどの位置情報付きでつぶやいた場合はここに緯度経度が含まれます。だがしかし、位置情報付でツイートを行うユーザーは皆無に等しいことが判明・・・(←ツイッター未経験のため知りませんでしたorz)。そこで形態素解析ですね!
形態素解析 - Wikipedia
Mecabという形態素解析エンジンを使用するのですが、Windowsで使用するにはMinGWやVisual Studioのインストール、コードの修正が必要でかなり面倒くさいので、Pythonモジュールはid:fgshunさんがコンパイルしたバイナリを使わせてもらいました。形態素解析エンジン MeCab 0.98pre3 野良ビルド - 銀月の符号
以下、導入方法です。
1.MeCabの本サイトでダウンロードしたWindows版のmecab-0.98.exeをインストール
2.形態素解析エンジン MeCab 0.98pre3 野良ビルドからダウンロードしたlibmecab-1.dll、MeCab.py、_MeCab.pydをパッケージフォルダ(Python2.7ならC:\Python27\Lib\site-packages)にコピー。
早速試してみます。
import MeCab
tagger = MeCab.Tagger("-Ochasen")
text = u"私は横浜に住んでいます。"
text_encode = text.encode('utf-8')
result = tagger.parse(text_encode)
result = result.decode('utf-8')
print result
「私は横浜に住んでいます。」という文章を形態素解析した結果が以下です。
私 ワタシ 私 名詞-代名詞-一般
は ハ は 助詞-係助詞
横浜 ヨコハマ 横浜 名詞-固有名詞-地域-一般
に ニ に 助詞-格助詞-一般
住ん スン 住む 動詞-自立 五段・マ行 連用タ接続
で デ で 助詞-接続助詞
い イ いる 動詞-非自立 一段 連用形
ます マス ます 助動詞 特殊・マス 基本形
。 。 。 記号-句点
「横浜」という文言は固有名詞-地域と認識されます。この仕組みを利用してツイート本文から地名を抜き出したいと思います。
ツイート本文からの位置情報推測
以下ツイート本文から地名を抜き出しMongoDBにデータを取込ます。
■GetLocationName.py
from pymongo import MongoClient
from collections import defaultdict
import MeCab
def main():
connect = MongoClient('localhost', 27017)
db = connect.footsal
tweetdata = db.tweetdata
for d in tweetdata.find({'spam':None,'retweeted_status': None},{'_id':1, 'text':1}):
ret = location_name_mecab(d['text'])
if len(ret) > 0:
tweetdata.update({'_id' : d['_id']},{'$push': {'location_name':ret[u'地域名称'][0]}})
def location_name_mecab(sentence):
t = MeCab.Tagger('-Ochasen')
sentence = sentence.replace('\n', ' ')
text = sentence.encode('utf-8')
node = t.parseToNode(text)
result_dict = defaultdict(list)
for i in range(140):
if node.surface != "":
if (node.feature.split(",")[1] == "固有名詞") and (node.feature.split(",")[2] == "地域"):
plain_word = node.feature.split(",")[6]
if plain_word !="*":
result_dict[u'地域名称'].append(plain_word.decode('utf-8'))
node = node.next
if node is None:
break
return result_dict
if __name__ == '__main__':
main()
地名から緯度経度に変換する
上記で地名を取得することができましたので、ジオコーディングの出番ですね。ジオコーディングについては以下参照です。
Pythonでジオコーディングをやってみる - GIS奮闘記
■GetLatLng.py
import geocoder
from pymongo import MongoClient
def main():
connect = MongoClient('localhost', 27017)
db = connect.footsal
tweetdata = db.tweetdata
for d in tweetdata.find({'location_name':{"$ne":None},'spam':None,'retweeted_status': None},{'_id':1, 'location_name':1}):
for name in d['location_name']:
loc = get_coordinate(name)
if loc.lat is not None:
tweetdata.update({'_id' : d['_id']},{'$push': {'lat':loc.lat, 'lng': loc.lng}})
loc = None
def get_coordinate(location_name):
try:
ret = geocoder.google(location_name)
except KeyError, e:
"KeyError(%s)" % str(e)
return
return ret
if __name__ == '__main__':
main()
これで緯度経度の取得完了です。
位置情報の可視化
それでは上記で取得した緯度経度を元に地図上にポイントをプロットします。今回はGoogleMapではなくMatplotlib basemapというライブラリを使用しますので、以下サイトでWheelをダウンロードしてpip install basemap-1.0.8-cp27-none-win32.whl のような感じでお願いします。
http://www.lfd.uci.edu/~gohlke/pythonlibs/
■PlotPoint.py
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pymongo import MongoClient
def main():
connect = MongoClient('localhost', 27017)
db = connect.footsal
tweetdata = db.tweetdata
fig = plt.figure(figsize=(10,10))
m = Basemap(projection='merc',llcrnrlat=25.836887 ,urcrnrlat=47.336893 ,\
llcrnrlon=126.919922,urcrnrlon=152.671875 ,lat_ts=20, resolution='f')
m.drawcoastlines( linewidth=0.25, color='k' )
m.fillcontinents(color='#eeeeee',lake_color='#ddeeff')
m.drawstates( linewidth=0.25, color='k' )
m.drawcountries()
m.drawmapboundary(fill_color='#ddeeff')
for d in tweetdata.find({'lat':{"$ne":None},'lng':{"$ne":None},'spam':None,'retweeted_status': None},{'_id':1, 'lat':1,'lng':1}):
lat = d['lat']
lon = d['lng']
t_x, t_y = m(lon,lat)
m.plot( t_x, t_y, "bo",markersize=5)
plt.show()
if __name__ == '__main__':
main()
これでポイントの配置が完了しました。結果を見てみます。
おぉーちゃんとプロットされましたね!今回はデータが少なかったので、あまり数がありませんが、本格的にデータを格納すれば分布図などができそうです(都市部を中心にプロットされそう)。
ただ、マップ上の表現としてはGoogleMapにしておけばよかったです(笑)。これはマップの鮮明さなどがちょっといまいちですかね。ただ、これはこれで便利なライブラリだと思うので、使いこなすことができたらまたおもしろいことができるかもしれませんね。
今回わかったことは
・ツイート情報に位置情報はほとんど付与されていないこと
・ツイート本文から位置情報を推測してもやはり正確な位置をつかむのは難しいこと(例えば、「川崎フロンターレのユニフォームを着て、フットサルなう」というツイートの場合、川崎として認識してしまう)
・形態素解析の素晴らしさ
・ツイート情報は1回のアクセスで100ツイートしか取得できず、アクセスも15分で180回という制限がある
・geocoderの使用制限(同一IPから一日2500件が上限らしいです)
・自分のプログラミング能力の低さ
あたりでしょうか。
色々なサイトを参考にさせてもらい、とても勉強になりました。今回の経験をもとにbotを作ってみてもおもしろいかも!
以上、本日は「PythonでTwitterデータを取得しマップ上で表現する」でした。