【Python】TwitterAPI でデータ収集(リプライツイート内容と画像)

f:id:albahoudori:20190826232258j:plain

1.目的
 とあるツイートのリプライ内容を収集する

2.収集対象
 ・ツイート者
 ・ツイート内容
 ・添付画像
 ・ツイート日時

3.参考
 ・Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報 - Qiita
 ・Python+Twitter API で佐久間まゆの画像を集める - Qiita
 ・Twitter APIでリプライを集めたい! - Qiita

4.実行結果例
f:id:albahoudori:20190826233717p:plain

5.設定ファイル
 4行は Twitter Developer Tool に登録した際の値になります
 ファイル名:setting.py

CONSUMER_KEY = "XXXXXXXXXXXXXXXXX"
CONSUMER_SECRET = "XXXXXXXXXXXXXXXXX"
ACCESS_TOKEN = "XXXXXXXXXXXXXXXXX"
ACCESS_TOKEN_SECRET = "XXXXXXXXXXXXXXXXX"
USERID = '@@(対象アカウント名)'
TWEETID = '(対象ツイートのID/WEB でツイートにアクセスした際のURLに値が確認できる)'
SEARCHRANGE_START = "YYYY-MM-DD(検索期間スタート)"
SEARCHRANGE_END = "YYYY-MM-DD(検索期間エンド)"

6.Python実行プログラム
 必要なライブラリをインストールしておくこと
 ファイル名:(get_replay.py)

from requests_oauthlib import OAuth1Session, OAuth1
import json
import os
import re
import urllib
import setting
import io
import requests
import sys

###変数
#ローカル用
parentDlDir = "download"
dlDir = "tmp"
dlDirPath = "C:\\"
#接続用
searchCount = 100 # 一回あたりの検索数(最大100/デフォルトは15)
maxCount = 100 # 検索回数の上限値(最大180/15分でリセット)
userId = setting.USERID
tweetId = setting.TWEETID
searchRange_start = setting.SEARCHRANGE_START
searchRange_end   = setting.SEARCHRANGE_END

### inner Class
# 集めた画像を格納するディレクトリの作成を行う
def createDlDir():
    global dlDir
    global dlDirPath
    if not os.path.isdir(parentDlDir):
        os.mkdir(parentDlDir)
    check_count = 0
    while True:
        dlDir = "/dir" + str(check_count)
        dlDirPath = parentDlDir + dlDir
        if not os.path.isdir(dlDirPath):
            os.mkdir(dlDirPath)
            return
        check_count += 1

#リプライツイートの検索
def getReplayTweet(auth, targetUserId):
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    url = "https://api.twitter.com/1.1/search/tweets.json?tweet_mode=extended&lang=ja&q="+targetUserId + "&since=" + searchRange_start + "&until=" + searchRange_end +"&count="+str(searchCount) 
    response = requests.get(url, auth=auth)
    data = response.json()['statuses']
    maxid = 0
    limit = 1
    replyCnt = 0
    check_image = []
    while True:
        # 制御
        if len(data) == 0:
            break
        if limit == maxCount:
            return 
        # リプライツイート取得
        for tweet in data:
             # 制御
             if not tweet['in_reply_to_status_id_str'] == tweetId:
                 continue
             # 基本情報取得
             replyTweetId  = str(tweet["id"])
             replyText = ''.join(tweet["full_text"].splitlines())
             replyUserName = tweet['user']['screen_name'] 

             replyAtTime = tweet["created_at"]
             replyCnt += 1
             replyCntPadded = '%04d' % replyCnt
             imageStatus = "NULL"
             
             #画像収集
             imageList = []
             imageNumber = 0
             try:
                 image_number = 1
                 media_list = tweet['extended_entities']['media']
                 for media in media_list:
                     image = media['media_url']
                     if image in check_image:
                         continue
                     with open(dlDirPath +"/"+ replyCntPadded + '_' + replyUserName + "_" + str(imageNumber) +"_"+ os.path.basename(image), 'wb') as f:
                         img = urllib.request.urlopen(image).read()
                         f.write(img)
                     imageList.append(str(image))
                     imageNumber += 1
                 imageStatus = "OK"
             except KeyError:
                 imageStatus = "--"
             except:
                 imageStatus = "NO"
             
             print(replyCntPadded + "\t" + imageStatus + "\t" + replyTweetId  + "\t" +  replyUserName   + "\t" +  replyText + "\t" + replyAtTime)
        
        # 次のXXX件を再度検索
        maxid = int(tweet["id"]) - 1
        nextUrl = url + "&max_id=" + str(maxid)
        response = requests.get(nextUrl, auth = auth)
        data = response.json()['statuses']
        limit += 1

### Main Class
if __name__ == '__main__':
    #イニシャライズ
    CK  = setting.CONSUMER_KEY
    CKS = setting.CONSUMER_SECRET
    AT  = setting.ACCESS_TOKEN
    ATS = setting.ACCESS_TOKEN_SECRET

    #フォルダ作成
    createDlDir()
    
    #接続開始
    auth = OAuth1(CK, CKS, AT, ATS)
    targetUserId = userId + ' exclude:retweets' # RTは除く
    targetUserId = urllib.parse.quote_plus(targetUserId)
    getReplayTweet(auth, targetUserId)

7.問題点
 たぶん以下のサイトにある理由だとは思いますが、一部ツイートが取得できませんでした。。。
 解決策がみつからなかった・・・
 【Twitter】取得できないリプライツイート問題 - Qiita