PythonでTwitterのデータを自動的に取得してデータベースに登録してみた

今回はPythonTwitterのデータを自動的に取得して、データベースに登録するスクリプトを作成してみました。

※プログラムとか初心者なので、コードとかぐちゃぐちゃです(笑)

最終的にはcronで毎分スクリプトを実行して、指定したワードでツイートを検索し、データベースに登録できるようになりました 。 スクリプトが走ると下記のようにツイートを取ってきて、データベースに登録してくれます。重複しているデータについては登録しないようにしています。

--------------------------------------------------------------
2017/12/25 00:00:02 [G20171225000002]ツイート取得バッチ開始
--------------------------------------------------------------
2017/12/25 00:00:05 検索ワード[モンハン]でTweetを検索します
2017/12/25 00:00:06 ツイートを100件取得しました
2017/12/25 00:00:06 データベースに接続します
2017/12/25 00:00:06 データベースにツイートを登録します
2017/12/25 00:00:06 [1/100] ツイートID:944945696188153856を登録しました
2017/12/25 00:00:06 [1/100] ユーザーID:4894539445は既に登録済みです
2017/12/25 00:00:06 [2/100] ツイートID:944945690416881665を登録しました
2017/12/25 00:00:06 [2/100] ユーザーID:453861709は既に登録済みです
2017/12/25 00:00:06 [3/100] ツイートID:944945674323353600を登録しました
2017/12/25 00:00:06 [3/100] ユーザーID:228667769は既に登録済みです
2017/12/25 00:00:06 [4/100] ツイートID:944945493515190272は既に登録済みです
2017/12/25 00:00:06 [5/100] ツイートID:944945469347655680は既に登録済みです
2017/12/25 00:00:06 [6/100] ツイートID:944945419553042432は既に登録済みです
2017/12/25 00:00:06 [7/100] ツイートID:944945388137545733は既に登録済みです
2017/12/25 00:00:06 [8/100] ツイートID:944945270319542273は既に登録済みです
2017/12/25 00:00:06 [9/100] ツイートID:944945249197027331は既に登録済みです
2017/12/25 00:00:06 [10/100] ツイートID:944945219795017729は既に登録済みです
2017/12/25 00:00:06 [11/100] ツイートID:944945203307094016は既に登録済みです
2017/12/25 00:00:06 [12/100] ツイートID:944945197162545154は既に登録済みです
2017/12/25 00:00:06 [13/100] ツイートID:944945177365331969は既に登録済みです
2017/12/25 00:00:06 [14/100] ツイートID:944945155311788032は既に登録済みです
2017/12/25 00:00:06 [15/100] ツイートID:944945093898678276は既に登録済みです
2017/12/25 00:00:06 [16/100] ツイートID:944945084084101120は既に登録済みです
2017/12/25 00:00:06 [17/100] ツイートID:944945054614921218は既に登録済みです
2017/12/25 00:00:06 [18/100] ツイートID:944944903758405637は既に登録済みです
2017/12/25 00:00:06 [19/100] ツイートID:944944896548380673は既に登録済みです
・
・
・
・
・
--------------------------------------------------------------
2017/12/25 00:00:06 [G20171225000002]ツイート取得バッチ終了
--------------------------------------------------------------

また、位置情報を含むツイートの場合は別途テーブルを用意して、保存しています。(何かしらに使って面白いことができそうなので、、、、)

twitter=# select * from places limit 10;
      tweet_id      | place_type |           name           |          full_name           | country_code | country 
--------------------+------------+--------------------------+------------------------------+--------------+---------
 934689168365576192 | city       | Chofu-shi                | Chofu-shi, Tokyo             | JP           | Japan
 934689532808650753 | city       | Minamiashigara-shi       | Minamiashigara-shi, Kanagawa | JP           | Japan
 934690949996756992 | city       | Nagaoka-shi              | Nagaoka-shi, Niigata         | JP           | Japan
 934692636300120064 | city       | Funabashi-shi            | Funabashi-shi, Chiba         | JP           | Japan
 934693197803950080 | city       | Kasuga-shi               | Kasuga-shi, Fukuoka          | JP           | Japan
 934693333108011008 | poi        | トリエ京王調布 A館       | トリエ京王調布 A館           | JP           | Japan
 934694073218113536 | admin      | Tōhoku                   | Tōhoku, Fukushima            | JP           | Japan
 934694637305864192 | city       | Fujieda-shi              | Fujieda-shi, Shizuoka        | JP           | Japan
 934694981356339200 | city       | Fukuoka City Hakata Ward | Fukuoka-shi Hakata, Fukuoka  | JP           | Japan
 934699282661126144 | city       | Kakogawa-shi             | Kakogawa-shi, Hyogo          | JP           | Japan
(10 行)

Twitter APIに登録していない人登録しておきましょう

Twitter Developer Platform — Twitter Developers

またpythonで使用するライブラリをインストール

pip install twitter

取得したデータを登録するデータベースを作成

今回はPostgreSQLを使ってやります。

postgres=# CREATE DATABASE twitter;
CREATE DATABASE

ツイートを保存するテーブル

CREATE TABLE raw_data (
    id bigint NOT NULL,
    id_str character varying(200),
    contributors text,
    coordinates text,
    entities text,
    extended_entities text,
    favorite_count integer,
    favorited boolean,
    geo text,
    in_reply_to_screen_name character varying(200),
    in_reply_to_status_id bigint,
    in_reply_to_status_id_str character varying(200),
    in_reply_to_user_id bigint,
    in_reply_to_user_id_str character varying(200),
    is_quote_status boolean,
    lang character varying(100),
    metadata character varying(400),
    possibly_sensitive boolean,
    quoted_status text,
    quoted_status_id bigint,
    quoted_status_id_str character varying(200),
    retweet_count integer,
    retweeted boolean,
    retweeted_status text,
    source text,
    text text,
    truncated boolean,
    user_id text,
    created_at timestamp without time zone,
    search_word character varying(100),
    batch_tag character varying(100)
);

CREATE TABLE metadata (
    id serial primary key,
    tweet_id bigint,
    iso_language_code varchar(100),
    result_type varchar(100)    
    );

ユーザーを登録するテーブル

CREATE TABLE twitter_user (
    id bigint primary key,
    name varchar(200),
    screen_name varchar(200),
    location varchar(100),
    description varchar(400),
    url varchar(300),
    entities text,
    protected boolean,
    followers_count int,
    friends_count int,
    listed_count int,
    created_at timestamp,
    favourites_count int,
    utc_offset int,
    time_zone varchar(100),
    geo_enabled boolean,
    verified boolean,
    statuses_count int,
    lang varchar(100),
    contributors_enabled boolean, 
    is_translator boolean, 
    is_translation_enabled boolean, 
    has_extended_profile boolean, 
    default_profile boolean, 
    default_profile_image boolean, 
    following boolean, 
    follow_request_sent boolean, 
    notifications boolean, 
    translator_type varchar(100)
    )

位置情報を登録するテーブル

CREATE TABLE places (
    tweet_id bigint NOT NULL,
    place_type character varying(100),
    name character varying(100),
    full_name character varying(200),
    country_code character varying(100),
    country character varying(100)
);

TwitterのOAuth

Twitterの認証は以下のようにします。各トークンはマイページで表示されているものを使用してください。

from twitter import Twitter, OAuth
twitterObj = Twitter(auth = OAuth(
    'ACCESS_TOKEN',
    'ACCESS_TOKEN_SECRET',
    'CONSUMER_KEY',
    'CONSUMER_SECRET'
    ))

ツイートを指定したワードで検索する

今回は「スタバ」というキーワードでツイートを検索してみましょう

searchWord = 'スタバ'
# Tweet検索
searchTweet = twitterObj.search.tweets(q = searchWord, count = 100) # 検索の取得件数は100件

ツイートの取得自体はこれだけでできます!

データベースに登録する流れは下記のソースでできます。 1度のスクリプトで複数の検索ワードを指定したい場合はリストでワードを指定すれば良いです。

各データの意味に付いては下記を参考にしてください。

Standard search API — Twitter Developers

from twitter import Twitter, OAuth
import pandas as pd
import psycopg2
import psycopg2.extras
from psycopg2.extensions import AsIs
from datetime import datetime
import dateutil.parser
import sys, os

sys.path.append('{0}/conf/'.format(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import conf

'''
fanctions
'''
def echo(text):
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"), text)

def echoLine():
    print('--------------------------------------------------------------')
    
def delColumn(obj, key):
    if key in obj.keys():
        del obj[key]

'''
main
'''
batch_tag = datetime.today().strftime('G%Y%m%d%H%M%S')

echoLine()
echo('[{0}]ツイート取得バッチ開始'.format(batch_tag))
echoLine()



echo('Twitter APIに接続します')
# Twitter API接続
twitterObj = Twitter(auth = OAuth(
    conf.ACCESS_TOKEN,
    conf.ACCESS_TOKEN_SECRET,
    conf.CONSUMER_KEY,
    conf.CONSUMER_SECRET
    ))

# 検索ワードのリスト
searchWords = ['スタバ', 'ドトール']
for searchWord in searchWords:
    echo('検索ワード[{0}]でTweetを検索します'.format(searchWord))
    # Tweet検索
    searchTweets = twitterObj.search.tweets(q = searchWord, count = 100)
    tweetCount = len(searchTweets['statuses'])
    echo('ツイートを{}件取得しました'.format(tweetCount))
    
    echo('データベースに接続します')
    # DBコネクション
    connection = psycopg2.connect(
        host = conf.ADVANCE_DB_HOST, 
        database = conf.ADVANCE_DB_NAME, 
        user = conf.ADVANCE_DB_USER, 
        password = conf.ADVANCE_DB_PASS
        )
    cursor = connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    
    echo('データベースにツイートを登録します')
    registCount = 1
    for status in searchTweets['statuses']:
        # 既に登録しているツイートはスキップ
        id = status['id']
        statement = 'SELECT id FROM raw_data WHERE id = %s'
        cursor.execute(statement, [status['id']])
        result = cursor.fetchone()
        if result is not None:
            echo('[{0}/{1}] ツイートID:{2}は既に登録済みです'.format(*[registCount, tweetCount, status['id']]))
            registCount += 1
            continue
     
        data = []
        columns = []
        for key in status.keys():
            value = str(status[key])
            
            if key == 'entities' or key == 'extended_entities':
                continue
            elif key == 'created_at':
                data.append(dateutil.parser.parse(status[key]))
            elif key == 'metadata':
                metadata = status['metadata']
                metadata['tweet_id'] = status['id']
                continue
            elif key == 'place':
                if status['place'] is not None:
                    place = status['place']
                    place['tweet_id'] = status['id']
                continue
            elif key == 'user':
                user = status['user']
                data.append(user['id'])
            elif value.find('{') > -1:
                data.append(str(status[key]))
            else :
                data.append(status[key])
    
            if key == 'user':
                columns.append('user_id')
            else:
                columns.append(key)
       
        # insert to raw_data
        columns.append('batch_tag')
        data.append(batch_tag)
        columns.append('search_word')
        data.append(searchWord)
        statement = 'INSERT INTO raw_data (%s) VALUES %s'
        cursor.execute(statement, (AsIs(','.join(columns)), tuple(data)))
        echo('[{0}/{1}] ツイートID:{2}を登録しました'.format(*[registCount, tweetCount, status['id']]))
        
        #insert to place
        if status['place'] is not None:
            delColumn(place, 'id')
            delColumn(place, 'url')
            delColumn(place, 'contained_within')
            delColumn(place, 'bounding_box')
            delColumn(place, 'attributes')
            statement = 'INSERT INTO places (%s) VALUES %s'
            cursor.execute(statement, (AsIs(','.join(place.keys())), tuple(place.values())))
        
        # insert to metadata
        statement = 'INSERT INTO metadata (%s) VALUES %s'
        cursor.execute(statement, (AsIs(','.join(metadata.keys())), tuple(metadata.values())))
        
        # userが登録されていなければ登録
        user_id = user['id']
        statement = 'SELECT id FROM twitter_user WHERE id = %s'
        cursor.execute(statement, [user_id])
        result = cursor.fetchone()
        if result is not None:
            echo('[{0}/{1}] ユーザーID:{2}は既に登録済みです'.format(*[registCount, tweetCount, user_id]))
            registCount += 1
            continue
        # insert to twitter_user
        delColumn(user, 'id_str')
        delColumn(user, 'entities')
        delColumn(user, 'profile_image_url')
        delColumn(user, 'profile_banner_url')
        delColumn(user, 'profile_background_color')
        delColumn(user, 'profile_background_image_url')
        delColumn(user, 'profile_background_image_url_https')
        delColumn(user, 'profile_background_tile')
        delColumn(user, 'profile_image_url_https')
        delColumn(user, 'profile_link_color')
        delColumn(user, 'profile_sidebar_border_color')
        delColumn(user, 'profile_sidebar_fill_color')
        delColumn(user, 'profile_text_color')
        delColumn(user, 'profile_use_background_image')
        statement = 'INSERT INTO twitter_user (%s) VALUES %s'
        cursor.execute(statement, (AsIs(','.join(user.keys())), tuple(user.values())))
        echo('[{0}/{1}] ユーザーID:{2}を登録しました'.format(*[registCount, tweetCount, user_id]))
        
        registCount += 1
    # コミット
    connection.commit()
echoLine()
echo('[{0}]ツイート取得バッチ終了'.format(batch_tag))
echoLine()