cojimaru BLOG

エンジニア支援のために技術情報を発信するブログ

Twitter APIによりTwitter検索ツールを作成してみた

f:id:cojimaru-chan:20210321103932p:plain

どうも、現役エンジニアのこじまるです。
今回はTwitter管理のために、Twitter APIを使用してTwitter検索ツールを自作します。



私はTwitterの運用にSocialDogを使っています。キーワードモニター機能が割と便利で利用しているのですが、無料アカウントの場合だとフォロー、いいね、リツイートの回数に制限があります。


SocialDogで有料アカウントになり余計な出費をするのは嫌なので、Twitter検索ツールを自作してみました。


Twitter検索ツール作成

要件定義

ツール要件
  • キーワードからTweetの検索ができること
  • 検索結果よりいいね、リツイートができること
  • ユーザが操作できること

環境構築

今回の実装では、下記を使用しました。

  • フロントエンド : HTML, JavaScript
  • サーバーサイド : Python(Flask)

HTML、JavaScriptではCDNJQueryなどを取得しています。そのため、必要なものは特にありません。Pythonでは、pipで下記のパッケージをインストールしています。

pip install flask
pip install requests
pip install requests-oauthlib

実装

キーワードからTweetの検索

f:id:cojimaru-chan:20210320194230p:plain
キーワードからTweetの検索の流れ
①Flaskへの検索依頼

HTMLからFlaskに対して、POST /searchTweetという形で検索依頼を投げます。


Flaskでは、POST /searchTweetでルーティングできるように、app.routeで定義する必要があります。この関数ではリクエストの内容を解析し、リクエストから検索条件を取得しています。

@app.route('/searchTweet',methods=["POST"])
def getTweet():
    print('Enter getTweet')
    data = request.get_data().decode('utf-8')
    tmp,query = data.split('=')
    q = "q={}".format(query)
    lang = "lang=ja"
    result_type = "result_type=recent"
    count = "count=10"
    entities = "include_entities=true"
②Tweeter APIにキーワードの検索依頼

①で説明した、getTweetの続きになります。①で取得した検索条件に基づき、Twitter API : GET search/tweetsを使ってTwitterからTweetを取得します。

    twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
    endpoint = "https://api.twitter.com/1.1/search/tweets.json?{}&{}&{}&{}&{}".format(q,lang,result_type,count,entities)
    res = twitter.get(endpoint)


Twitter API : GET search/tweetsとGET statuses/lookup*1で取得したデータから、下記の情報を抽出し、リストに格納します。

【userオブジェクト】

名前 内容
name ネーム
screen_name スクリーンネーム
id 数値型のユーザーID
profile_image_url_https プロフィール画像SSL用のURL

Tweetオブジェクト】

名前 内容
id_str 文字列型のツイートID
text ツイートの本文
created_at Tweetが投稿された日時
favorited いいねされているかどうかの状態
favorite_count いいねされた数
retweeted リツイートされているかどうかの状態
retweet_count リツイートされた数
retweeted_status 元のツイートの情報


ここで、retweeted_statusは引用なし(公式)リツイートの場合のみ取得できます。


userオブジェクト・Tweetオブジェクトの内容はこちらの記事を参考にしました。
syncer.jp

③検索結果の表示依頼

②で取得したuserオブジェクト・Tweetオブジェクトの情報を集積したリスト(tweet_list)をrender_templateメソッドの引数に指定します。

@app.route('/searchTweet',methods=["POST"])
def getTweet():
    ...
    print("Exit getTweet")
    return render_template('searchTweet.html', tweet_list=tweet_list)


今回は②で取得した検索結果を表示するために、tweet_listの配列数分ループさせるように実装しています。{{list.profile_image_url_https}}と記述することで、list内の要素にアクセスできます。

{% extends "layout.html" %}
{% block content %}
{% for list in tweet_list %}
<div class="twitter__block">
    <figure>
        <img src={{list.profile_image_url_https}} />
    </figure>
    ...
</div>
{% endfor %}
{% endblock %}


いいね、リツイートは、検索実行時のいいね、リツイートの状態に基づきON、あるいはOFFにして表示する必要があります。そのため、下記のようにlist.favoritedの値に応じて、spanタグのclass属性の値を切り替えるようにしました。

{% if list.favorited %}
    <span id={{list.tweet_id}} class="LikesIcon on">
        <i class="fas fa-heart LikesIcon-fa-heart heart">{{list.favorite_count}}</i>
    </span>
{% else %}
    <span id={{list.tweet_id}} class="LikesIcon">
        {% if list.favorite_count > 0 %}
            <i class="far fa-heart LikesIcon-fa-heart">{{list.favorite_count}}</i>    
        {% else %}
            <i class="far fa-heart LikesIcon-fa-heart"></i>
        {% endif %}
    </span>
{% endif %}

検索結果からいいね、リツイートを行う

いいね・リツイートを行う

いいね・リツイートを行うために、下記のようにクリックイベントを使用します。(いいねとリツイートの実装はあまり変わらないため、今回はいいねの場合のみ説明します。)検索結果のあるツイートのいいねの状態がONであれば、いいねを外します。いいねの状態がOFFであれば、いいねを行います。

$('.LikesIcon').on('click', function() {
    let $btn = $(this);
    tweet_id = $btn.attr('id')
    // Likeボタンがonクラス持っていたら
    if ($btn.hasClass('on')) {
        $btn.removeClass('on');
        // 白抜きアイコンに戻す
        $btn.children("i").attr('class', 'far fa-heart LikesIcon-fa-heart');
        $.ajax({
            url: 'favorites',
            type:'DELETE',
            dataType: 'json',
            data : {'tweet_id' : tweet_id},
            timeout:3000,
        }).done(function(data) {
            if(data['favorite_count'] != 0)
                $btn.children("i").text(data['favorite_count'])
            else
                $btn.children("i").text("")  
        })
    } else {
        $btn.addClass('on');
        // ①アイコンを変更する
        // far fa-heart(白抜きアイコン)
        // ⇒ fas fa-heart(背景色つきアイコン)
        // ②アニメーション+アイコン色変更用のheartクラスを付与する
        $btn.children("i").attr('class', 'fas fa-heart LikesIcon-fa-heart heart');
        $.ajax({
            url: 'favorites',
            type:'POST',
            dataType: 'json',
            data : {'tweet_id' : tweet_id},
            timeout:3000,
        }).done(function(data) {
            $btn.children("i").text(data['favorite_count'])
        })
    }
});


JavaScript側で細々した操作をしたくないので、Ajaxでリクエストを送る先をFlaskにしています。Flaskでは、いいねを行う場合にはPOST favorites/createを実行し、いいねを外す場合にはPOST favorites/destroyを実行します。

@app.route('/favorites',methods=["POST"])
def setFavorites():
    twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
    tweet_id = request.form.get('tweet_id')
    endpoint = "https://api.twitter.com/1.1/favorites/create.json?id={}".format(tweet_id)
    res = twitter.post(endpoint)
    json_res = res.json()
    status = getStatus(tweet_id)
    return status

@app.route('/favorites',methods=["DELETE"])
def deleteFavorites():
    twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
    tweet_id = request.form.get('tweet_id')
    endpoint = "https://api.twitter.com/1.1/favorites/destroy.json?id={}".format(tweet_id)
    res = twitter.post(endpoint)
    json_res = res.json()
    status = getStatus(tweet_id)
    return status


Twitterのハートボタンのアニメーションは下記記事を参考にさせていただきました。
yuyauver98.me

コード

コード全文はGithubにアップロードしているので、こちらからご確認ください。
github.com

結果


まとめ

今回Twitter APIを使用して、Twitter検索ツールを作成してみました。自動実行だと楽だったのですが、UIを持たせる仕様にしたかったので、それに余計に時間がかかってしまったと思います。また、普段使用しないTwitter APIの仕様を理解し、実装するのは結構難しいと感じました。


現状のツールでは、使用出来る機能が少なかったり、UIなども完全なものではないので今後改善を図りたいと思います。こちらの関連で何か出来上がりましたら、続編という形で掲載しますので、ご期待ください。


pythonTwitter APIを使用した自動投稿の記事も書いていますので、ぜひ見てください。
cojimaru-chan.hatenablog.com

*1:GET search/tweetsで取得したRetweetのデータとTwitterの結果を見比べたところ、favoritedが一致しなかったため、GET statuses/lookup - 複数のツイートを取得する も利用しています。