AWS Comprehend + Lambda + NewRelicで毎日のツイートの感情分析を可視化してみた

## 概要
AWSのComprehendを使って自分のツイートの感情分析を行い、分析結果の数値をNewRelicに送信してダッシュボードを作ります。

## 完成図

こんな感じに毎日のツイートのポジティブ・ネガティブを可視化させます。

![スクリーンショット 2022-12-14 20.32.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/52c09bec-0910-a846-a4ea-6161446ecb2c.png)


## 用意するもの

- NewRelicのライセンスキー
- Twitter APIのキー
- Lambda関数

Twitter APIキーの発行やNewRelicの使い方詳細の説明は割愛します。

## Lambdaで関数作成

コードの全体像としてはこんな感じです

```python
import tweepy
import boto3
import os
import datetime as dt
import json
import requests

# 環境変数を読みこむ
TWITTER_API_KEY = os.environ["TWITTER_API_KEY"]
TWITTER_API_SECRET = os.environ["TWITTER_API_SECRET"]
TWITTER_BEARER_TOKEN = os.environ["TWITTER_BEARER_TOKEN"]
TWITTER_ACCESS_TOKEN = os.environ["TWITTER_ACCESS_TOKEN"]
TWITTER_ACCESS_SECRET = os.environ["TWITTER_ACCESS_SECRET"]
NEWRELIC_KEY = os.environ["NEWRELIC_KEY"]
NEWRELIC_ENDPOINT = os.environ["NEWRELIC_ENDPOINT"]

# Twitter APIと連携
client = tweepy.Client(bearer_token=TWITTER_BEARER_TOKEN,
                        consumer_key=TWITTER_API_KEY,
                        consumer_secret=TWITTER_API_SECRET,
                        access_token=TWITTER_ACCESS_TOKEN,
                        access_token_secret=TWITTER_ACCESS_SECRET
                    )
                    
comprehend = boto3.client('comprehend')

def get_formatted_datetime(fmt, now):
    return dt.datetime.strftime(now, fmt)
    
def get_sentiment_score(target):
    sentiment = comprehend.detect_sentiment(Text=target, LanguageCode='ja')
    return sentiment.get('SentimentScore')
    
## NewRelicにデータを送信する関数
def post_newrelic(data):
    headers = {"X-Insert-Key": NEWRELIC_KEY}
    res = requests.post(NEWRELIC_ENDPOINT, headers=headers, json=data)
    return res
    
def lambda_handler(event,context):

    # twitterのcreated_atはUTCになってる。JSTで時刻を合わせてやる
    fmt = "%Y-%m-%dT15:00:00Z"
    JST = dt.timezone(dt.timedelta(hours=9), 'JST')
    now = dt.datetime.now(JST)
    start_today = get_formatted_datetime(fmt, now - dt.timedelta(days=1))
    now_formatted = get_formatted_datetime("%Y-%m-%dT%H:%M:%SZ",now)

    user = client.get_me()

    #　ツイートを取得する
    tweets = client.get_users_tweets(id = user.data.id,
                                    exclude=("replies"),
                                    start_time=start_today,
                                    end_time=now_formatted
                                    )
    tweets_data = tweets.data

    data = []
    if tweets_data != None:
        for i, tweet in enumerate(tweets_data):
            result = get_sentiment_score(tweet.text)

            score = result['Positive'] - result['Negative']
            label = max(result, key=result.get)
            
            if label == 'Positive':
                label = 'ポジティブ'
            elif label == 'Negative':
                label = 'ネガティブ'
            else:
                label = 'その他'
                
            d = {}
            d["eventType"] = "twitter"
            d["positive_score"] = result['Positive']
            d["negative_score"] = result['Negative']
            d["label"] = label
            d["tweet"] = tweet.text
            data.append(d)
            
		
    return post_newrelic(data)
```

### Comprehend APIで返ってくる値

取得したツイートをComprehend APIに投げると
```python
result = get_sentiment_score(tweet.text)
```

以下のような値が取れます。
```python
{
  "Text": "ツイート内容", 
  "Positive": 0.xxxxxxx, 
  "Negative": 0.xxxxxxx, 
  "Neutral": 0.xxxxxxx, 
  "Mixed": 0.xxxxxxxx
}
```

Positive, Negative, Neutral, Mixedの4つの感情スコアが返され、それぞれ1に近いほど大きいスコアとされています。

### NewRelicに送信する値を定義

今回は以下4つをパラメータとしてNewRelicに送信します。

ポジティブ指数 **(positive_score)**
ネガティブ指数 **(negative_score)**
感情ラベル **(label)**
ツイート内容 **(tweet)**

```python
d = {}
d["eventType"] = "twitter"
d["positive_score"] = result['Positive']
d["negative_score"] = result['Negative']
d["label"] = label
d["tweet"] = tweet.text
            
```

感情ラベル(label)は、
Positiveのスコアが最も高得点なツイートを **「ポジティブ」**
Negativeのスコアが最も高得点なツイートを **「ネガティブ」**
それ以外を **「その他」**　として設定します。

```python
label = max(result, key=result.get)
            
if label == 'Positive':
    label = 'ポジティブ'
elif label == 'Negative':
    label = 'ネガティブ'
else:
    label = 'その他'
```
## EventBridgeの設定
作成したLambda関数を毎日呼び出すトリガーをEventBridgeで作成します。


Lambda関数のページからトリガーを追加
![スクリーンショット 2022-12-14 20.59.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/49506bb2-8c50-a455-9c0e-56109cc9377e.png)

トリガーの種類として**EventBridge**を選択
![スクリーンショット 2022-12-14 20.56.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/ae86f635-d4cb-bcf2-c94b-e440bbca11e0.png)

今回は **「毎日21時に実行する」** ルールを設定します
![スクリーンショット 2022-12-14 20.58.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/a6f05243-a6f6-c14c-82e0-9677885c6086.png)

これでEventBridgeの設定は完了です。

## NewRelicでグラフを作成

NRQLを使って分析結果をいい感じに表示させるグラフを作成します

**直近1週間のツイートのポジティブ指数・ネガティブ指数**
```sql
SELECT average(positive_score) as 'ポジティブ指数', average(negative_score) as 'ネガティブ指数' FROM twitter since 1 week ago
```
![スクリーンショット 2022-12-14 20.18.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/06f39472-3d5f-0b61-3e3f-8c2d95fea837.png)

**1日ごとのポジティブツイート/ネガティブツイートの数**

```sql
SELECT count(tweet) FROM twitter where label != 'その他' facet label since 2 week ago TIMESERIES 1 day
```
![スクリーンショット 2022-12-14 20.33.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/3398b77c-88f1-852c-28bc-220ab50d151e.png)

**1週間のポジティブ/ネガティブツイートの比率**
```sql
SELECT count(*) FROM twitter where label != 'その他' facet label since 1 week ago
```
![スクリーンショット 2022-12-14 20.19.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/9e0b23de-d947-f726-ca7a-08ead9b33dd0.png)

**ツイートごとの分析結果を表示**
``` sql
SELECT label, positive_score , negative_score, tweet FROM twitter since 1 day ago order by timestamp desc
```
![スクリーンショット 2022-12-14 20.40.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689205/6ab285c0-024f-3935-2a3f-56bbb759b454.png)


## 所感
- ネガティブなツイートが多かったらAPI経由で自動でツイ消しのような仕組みも作りたい
    - NewRelicの**Alert Condition + Workflow**を使用すれば作れそう
- 割とネガティブっぽいツイートが多いことに気づく
    - 前向きに生きよう！

## 参考・関連記事

https://python-man.club/python_twitter_api_tweet/

https://xp-cloud.jp/blog/2021/09/06/12183/

https://docs.newrelic.com/jp/docs/data-apis/ingest-apis/event-api/introduction-event-api/

https://docs.newrelic.com/jp/docs/query-your-data/nrql-new-relic-query-language/get-started/introduction-nrql-new-relics-query-language/