ツイートをセンチメントスコアと組み合わせると、ツイートを定量的に評価できます。気分を尋ねる質問の後ろにいくつかのデータを配置するには、Twitterの最新の検索エンドポイントであるPythonを使用して過去7日間のツイートを確認したり、Microsoft AzureのText Analytics Cognitive Serviceを使用して言語を検出し、センチメントスコアを確認したりする方法があります。このチュートリアルでは、過去7日間のツイートを取り出してスコアを付け、1週間がどのようなものだったかを正確に把握できるコードの作成方法について説明します。コードの完全版はv2というフォルダーにあります。 

 

設定

 

始める前に、以下のものが揃っていることを確認してください。

 

 

このプロジェクト用のディクショナリーを作成する必要があります。ターミナルで次のように入力すると、新しいディレクトリが作成され、現在のディレクトリから作成した新しいディレクトリに変更されます。また、トークンとシークレットの保存に使用する、新しいPythonファイルとYAML構成ファイルを作成します。

      mkdir how-positive-was-your-week
cd how-positive-was-your-week
touch week.py
touch config.yaml

    


これで、お好みのテキストエディターを使用して、config.yaml構成ファイルを設定できるようになりました。これを設定して、xの部分を独自のベアラートークンとサブスクリプションキーに置き換えます。

 

      search_tweets_api:
  bearer_token: xxxxxxxxxxxxxxxxxxxxxxx
azure:
  subscription_key: xxxxxxxxxxxxxxxxxxxxxxx
    

また、RequestsPyYaMLPandasのライブラリをインストールする必要があります。Requestsは、TwitterおよびAzureのエンドポイントと、データの形成に使用されるPandasにHTTPリクエストを行うために使用されます。PyYaMLは、キーとトークンを格納する.yamlファイルを解析するために使用されます。Pandasは、データの操作と形成に使用されます。

 

week.pyファイルを開き、使用するライブラリをすべてインポートします。RequestsとPandasに加えて、Pythonの標準ライブラリの一部であるjsonastのパッケージもインポートできるため、事前にインストールする必要はありません。さらに、Pandasはエイリアスpdを使用してインポートされるため、ライブラリを呼び出すたびに単語全体を入力する必要はありません。

      import requests
import pandas as pd
import json
import ast
import yaml
    

Twitter APIに接続する前に、適切なデータを取得できるように、適切なフィールドを持つURLを設定する必要があります。まず、create_twitter_urlという関数を作成する必要があります。この関数で、ユーザー名の変数を宣言することで、jessicagarsonを独自のユーザー名に置き換えることができます。max_resultsは1~100の範囲で指定できます。指定した1週間に100件を超えるツイートがあるユーザー名を使用している場合は、ページネーションを処理するロジックを組み込むか、search-tweets-pythonなどのライブラリを使用することをお勧めします。URLは、結果の最大数と、特定のユーザー名からのツイートを検索することを示すクエリを含むようにフォーマットする必要があります。フォーマットされたURLは、後でGETリクエストを行うために必要になるため、urlという変数で返します。

      def create_twitter_url():
    handle = "jessicagarson"
    max_results = 100
    mrf = "max_results={}".format(max_results)
    q = "query=from:{}".format(handle)
    url = "https://api.x.com/2/tweets/search/recent?{}&{}".format(
        mrf, q
    )
    return url
    


作成するURLは、次のようなものになります。
https://api.x.com/2/tweets/search/recent?max_results=100&query=from:jessicagarson

 

リツイートやメディアを含むツイートを除外したい場合は、クエリを調整できます。クエリにフィールド拡張を追加することで、Twitter APIで返されるデータを調整できます。PostmanInsomniaなどのRESTクライアントを使用すると、コードの記述を開始する前に、返されるデータを確認して調整を行うのに役立ちます。v2エンドポイント用のPostmanコレクションもあります。

 

main関数の設定

 

ファイルの下部で、作成したすべての関数の呼び出しに使用するmain関数の設定を開始できます。先ほど作成した関数を追加し、if __name__ == "__main__"ステートメントを使用して関数を呼び出すことができます。

      def main():
    url = create_twitter_url()


if __name__ == "__main__":
    main()

    

Twitter APIの認証と接続

 

config.yamlを設定する際に作成した構成ファイルにアクセスするには、YAMLファイルを読み込んで内容を保存するprocess_yamlという関数を定義します。

      def process_yaml():
    with open("config.yaml") as file:
        return yaml.safe_load(file)
    

main関数では、これをdataという名前の変数に保存できます。これで、main関数にはURL用とdata用の2つの変数が含まれるはずです。

      def main():
    url = create_twitter_url()
    data = process_yaml()

    

config.yamlファイルからベアラートークンにアクセスするには、次の関数を使用できます。

      def create_bearer_token(data):
    return data["search_tweets_api"]["bearer_token"]
    

先ほどと同様に、bearer_tokenという変数をmain関数に追加します。

      def main():
    url = create_twitter_url()
    data = process_yaml()
    bearer_token = create_bearer_token(data)
    

Twitter APIに接続するには、twitter_auth_and_connectという関数を作成し、bearer_tokenとurlを渡すようにヘッダーをフォーマットします。この時点で、requestパッケージを使用してGETリクエストを行い、Twitter APIに接続します。

      def twitter_auth_and_connect(bearer_token, url):
    headers = {"Authorization": "Bearer {}".format(bearer_token)}
    response = requests.request("GET", url, headers=headers)
    return response.json()
    

この関数で返すオブジェクトは、次のようなペイロードです。

      {'data': [{'id': '1272881032308629506', 'text': '@nomadaisy @kndl I just want to do deals with you'}, {'id': '1272880943687258112', 'text': '@nomadaisy @kndl I live too far away to hang responsibly with y’all 😬😭'}, {'id': '1272711045606408192', 'text': '@Babycastles https://t.co/Yfj8SJAnpG'}, {'id': '1272390182231330816', 'text': '@replylord Haha, I broke a glass in your honor today and all so I think I do read your Tweets'}, {'id': '1271810907274915840', 'text': '@replylord I like that I’m the only like here.'}, {'id': '1271435152183476225', 'text': '@Arfness @ChicagoPython @codewithbri @WeCodeDreams @agfors The video seems to be available https://t.co/GojUGdulkP'}, {'id': '1271111488024064001', 'text': 'RT @TwitterDev: Tune in tonight and watch as @jessicagarson takes us through running your favorite Python package in R. 🍿\n\nLearn how to use…'}, {'id': '1270794941892046848', 'text': 'RT @ChicagoPython: Chicago Python will be live-streaming tmrw night!\n\nOur talks:\n- How to run your favorite Python package in R by @jessica…'}, {'id': '1270485552488427521', 'text': "Speaking virtually at @ChicagoPython's __main__ meeting on Thursday night. I'll be showing how to run your favorite Python package in R. https://t.co/TnqgO80I3t"}], 'meta': {'newest_id': '1272881032308629506', 'oldest_id': '1270485552488427521', 'result_count': 9}}

    

これで、main関数が更新され、次のようになります。

      def main():
    url = create_twitter_url()
    data = process_yaml()
    bearer_token = create_bearer_token(data)
    res_json = twitter_auth_and_connect(bearer_token, url)
    

言語の生成

 

最新の検索ペイロードを使用してペイロードから言語を取得することは可能で、この方法を使用するコードのバージョンはありますが、Azureにもユーザーの言語を推定するエンドポイントが用意されています。使用前に、データが言語検出エンドポイントに接続するための正しい形になっていることを確認する必要があるため、Azureのクイックスタートガイドで概説されている形式に合うようにデータをフォーマットします。そのためには、ツイートとIDを含む呼び出されたデータ内のオブジェクトをdata_onlyという変数に分離する必要があります。ツイートデータを正しいフォーマットにするための文字列フォーマットや、文字列をディクショナリーに変換するために必要なその他のフォーマットを行う必要があります。jsonおよびastライブラリを使用すると、このコンバージョンに役立ちます。

      def lang_data_shape(res_json):
    data_only = res_json["data"]
    doc_start = '"documents": {}'.format(data_only)
    str_json = "{" + doc_start + "}"
    dump_doc = json.dumps(str_json)
    doc = json.loads(dump_doc)
    return ast.literal_eval(doc
    

Azureに接続するには、Twitter API URLで行った手順と同じように、URLを調整してデータをフォーマットする必要があります。言語とセンチメントエンドポイントの両方からデータを取得するためのURLを設定できます。認証情報はconfig.yamlから解析され、Azureエンドポイントの認証に渡されます。

      def connect_to_azure(data):
    azure_url = "https://week.cognitiveservices.azure.com/"
    language_api_url = "{}text/analytics/v2.1/languages".format(azure_url)
    sentiment_url = "{}text/analytics/v2.1/sentiment".format(azure_url)
    subscription_key = data["azure"]["subscription_key"]
    return language_api_url, sentiment_url, subscription_key
    

さらに、サブスクリプションキーをリクエストに必要な形式で渡すことで、Azureに接続するためのヘッダーを作成する関数を作成します。

      def azure_header(subscription_key):
    return {"Ocp-Apim-Subscription-Key": subscription_key}
    

この時点で、Azure APIにPOSTリクエストを行う準備が整い、ツイートの言語を生成できるようになります。

      def generate_languages(headers, language_api_url, documents):
    response = requests.post(language_api_url, headers=headers, json=documents)
    return response.json()

    

以下のようなJSON応答が返されます。

 

      {'documents': [{'id': '1272881032308629506', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1272880943687258112', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1272711045606408192', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1272390182231330816', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1271810907274915840', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1271435152183476225', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1271111488024064001', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1270794941892046848', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}, {'id': '1270485552488427521', 'detectedLanguages': [{'name': 'English', 'iso6391Name': 'en', 'score': 1.0}]}], 'errors': []}

    

また、作成した新しい関数を含むようにmain関数を更新する必要があります。以下のようになります。

      def main():
    url = create_twitter_url()
    data = process_yaml()
    bearer_token = create_bearer_token(data)
    res_json = twitter_auth_and_connect(bearer_token, url)
    documents = lang_data_shape(res_json)
    language_api_url, sentiment_url, subscription_key = connect_to_azure(data)
    headers = azure_header(subscription_key)
    with_languages = generate_languages(headers, language_api_url, documents)

    

センチメントスコアの取得

 

Azureのエンドポイントを使用してセンチメントスコアを生成する前に、ツイートデータと生成された言語を含むデータを結合する必要があります。このデータコンバージョンプロセスを支援するために、Pandasを使用できます。検出された言語を含むjsonオブジェクトをデータフレームに変換できます。言語の略語のみが必要なため、リスト内包表記を実行して、言語の略語を含むiso6391Nameを取得できます。iso6391Nameは、リスト内のディクショナリーに含まれており、そのリストは言語データを含むデータフレーム内にあります。ツイートデータをデータフレームに変換し、そのデータフレームにツイートの言語の略語を添付することもできます。そこから、そのツイートデータをJSON形式で送信できます。

      def combine_lang_data(documents, with_languages):
    langs = pd.DataFrame(with_languages["documents"])
    lang_iso = [x.get("iso6391Name")
                for d in langs.detectedLanguages if d for x in d]
    data_only = documents["documents"]
    tweet_data = pd.DataFrame(data_only)
    tweet_data.insert(2, "language", lang_iso, True)
    json_lines = tweet_data.to_json(orient="records")
    return json_lines
    

データをディクショナリー形式に変換する方法と同様に、ペイロードの前にある単語documents:をキーとして、センチメントスコアを取得します。

      def add_document_format(json_lines):
    docu_format = '"' + "documents" + '"'
    json_docu_format = "{}:{}".format(docu_format, json_lines)
    docu_align = "{" + json_docu_format + "}"
    jd_align = json.dumps(docu_align)
    jl_align = json.loads(jd_align)
    return ast.literal_eval(jl_align)
    

これで、データはAzureのセンチメントエンドポイントを呼び出すのに適した形式になりました。connect_to_azure関数で定義したセンチメントエンドポイントに対してPOSTリクエストを行うことができます。

      def sentiment_scores(headers, sentiment_url, document_format):
    response = requests.post(
        sentiment_url, headers=headers, json=document_format)
    return response.json()
    

返されるJSON応答は、以下のペイロードのようになります。

      {'documents': [{'id': '1272881032308629506', 'score': 0.18426942825317383}, {'id': '1272880943687258112', 'score': 0.0031259357929229736}, {'id': '1272711045606408192', 'score': 0.7015109062194824}, {'id': '1272390182231330816', 'score': 0.8754926323890686}, {'id': '1271810907274915840', 'score': 0.19140595197677612}, {'id': '1271435152183476225', 'score': 0.7853382229804993}, {'id': '1271111488024064001', 'score': 0.7884223461151123}, {'id': '1270794941892046848', 'score': 0.8826596736907959}, {'id': '1270485552488427521', 'score': 0.8784275054931641}], 'errors': []}

    

これで、main関数は以下のようになります。

 

      def main():
    url = create_twitter_url()
    data = process_yaml()
    bearer_token = create_bearer_token(data)
    res_json = twitter_auth_and_connect(bearer_token, url)
    documents = lang_data_shape(res_json)
    language_api_url, sentiment_url, subscription_key = connect_to_azure(data)
    headers = azure_header(subscription_key)
    with_languages = generate_languages(headers, language_api_url, documents)
    json_lines = combine_lang_data(documents, with_languages)
    document_format = add_document_format(json_lines)
    sentiments = sentiment_scores(headers, sentiment_url, document_format)
    

平均センチメントスコアの取得

 

平均センチメントスコアを取得するには、AzureセンチメントエンドポイントからのJSON応答をデータフレームに変換し、スコアというタイトルの列の平均を計算します。

      def mean_score(sentiments):
    sentiment_df = pd.DataFrame(sentiments["documents"])
    return sentiment_df["score"].mean()
    

平均スコアを取得したら、1週間がどれだけポジティブであったかを正確に把握するための論理ステートメントを作成できます。

      def week_logic(week_score):
    if week_score > 0.75 or week_score == 0.75:
        print("You had a positive week")
    elif week_score > 0.45 or week_score == 0.45:
        print("You had a neutral week")
    else:
        print("You had a negative week, I hope it gets better")

    

ファイルのmainステートメントの最終バージョンは次のようになります。

 

      def main():
    url = create_twitter_url()
    data = process_yaml()
    bearer_token = create_bearer_token(data)
    res_json = twitter_auth_and_connect(bearer_token, url)
    documents = lang_data_shape(res_json)
    language_api_url, sentiment_url, subscription_key = connect_to_azure(data)
    headers = azure_header(subscription_key)
    with_languages = generate_languages(headers, language_api_url, documents)
    json_lines = combine_lang_data(documents, with_languages)
    document_format = add_document_format(json_lines)
    sentiments = sentiment_scores(headers, sentiment_url, document_format)
    week_score = mean_score(sentiments)
    print(week_score)
    week_logic(week_score)
    

これで、ターミナルに次のように入力してコードを実行できるようになります。

 

      python3 week.py

    

センチメントスコアに応じて、これと似た内容がターミナル出力に表示されます。

 

      0.6470708809792995
You had a neutral week
    

次の手順

 

このコードサンプルは簡単に拡張できるため、どのツイートが最もポジティブまたはネガティブだったかを確認したり、週ごとの変化を視覚化して追跡したりできます。

 

途中で問題が発生した場合はフォーラムでお知らせください。また、これが何かを作成するきっかけになった場合は、@TwitterDevでツイートしてください。このチュートリアルの作成にあたって、Twitter API以外のライブラリやツールをいくつか使用しましたが、ニーズや要件が異なる場合がありますので、それらのツールが適切かどうかを個別に評価してください。Twitterは、上記のサードパーティサービスを運営または管理しておらず、それらのサービスにはツールやその他の機能の使用に関する別個の規約がある場合があります。