ツイートをセンチメントスコアと組み合わせると、ツイートを定量的に評価できます。気分を尋ねる質問の後ろにいくつかのデータを配置するには、Twitterの最新の検索エンドポイントであるPythonを使用して過去7日間のツイートを確認したり、Microsoft AzureのText Analytics Cognitive Serviceを使用して言語を検出し、センチメントスコアを確認したりする方法があります。このチュートリアルでは、過去7日間のツイートを取り出してスコアを付け、1週間がどのようなものだったかを正確に把握できるコードの作成方法について説明します。コードの完全版はv2というフォルダーにあります。
設定
始める前に、以下のものが揃っていることを確認してください。
- Python 3がインストールされている。
- Twitter開発者アカウント: アカウントがない場合は申請してください。
- プロジェクトとアプリ。Twitter開発者アカウントで作成できます。
- アプリの登録に必要なベアラートークン。ベアラートークンは、開発者ポータルで提供されているアプリのキーとトークンを使用して確認できます。
- Microsoft AzureのText Analytics Cognitive Serviceを使用できるアカウントと作成されたエンドポイント。Text Analytics APIを呼び出す方法については、Microsoftのクイックスタートガイドを参照してください。
このプロジェクト用のディクショナリーを作成する必要があります。ターミナルで次のように入力すると、新しいディレクトリが作成され、現在のディレクトリから作成した新しいディレクトリに変更されます。また、トークンとシークレットの保存に使用する、新しい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
また、Requests、PyYaML、Pandasのライブラリをインストールする必要があります。Requestsは、TwitterおよびAzureのエンドポイントと、データの形成に使用されるPandasにHTTPリクエストを行うために使用されます。PyYaMLは、キーとトークンを格納する.yaml
ファイルを解析するために使用されます。Pandasは、データの操作と形成に使用されます。
week.py
ファイルを開き、使用するライブラリをすべてインポートします。RequestsとPandasに加えて、Pythonの標準ライブラリの一部であるjson
とast
のパッケージもインポートできるため、事前にインストールする必要はありません。さらに、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で返されるデータを調整できます。PostmanやInsomniaなどの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は、上記のサードパーティサービスを運営または管理しておらず、それらのサービスにはツールやその他の機能の使用に関する別個の規約がある場合があります。