AWS Lambdaで列車運行情報をLINEへ通知【Python】

# 背景

この記事の処理内容を、スクレイピングからAPIでの情報取得へ変更したことを紹介します。
👇

https://qiita.com/_whitecat_22/items/1fc804847dce27d5fb94

# 1.環境

- Python 3.9.x
- AWS Lambda
- Amazon EventBridge　...定期的に実行するため
- Amazon Simple Notification Service (SNS)　...終了通知をCloudWatchへ渡すため
- Amazon CloudWatch　...ロギング
- Amazon Simple Storage Service (S3)　...Lambda関数のアップロード先
- [LINE公式アカウント](https://www.linebiz.com/jp/service/line-official-account/)

# 2.開発

## 2.1.Pythonコード

- スクレイピングの処理を、公共交通オープンデータセンター 開発者サイトから登録すると利用可能なAPIへ変更しました。
 ※ユーザー登録申請から承認までは、2営業日ほど。

https://developer.odpt.org/

- 主な利用ライブラリは以下の2つ。
    - pytz
　　　UTC(協定世界時)→JST(日本標準時)へ変換する際に利用
　　　※Linuxの時間はUTCで管理されているため、通知前にJSTへ変換する必要あり
　　　→APIで取得時間がJSTで返ってくるため、そちらを利用しても可。
    - line-bot-sdk

```Python:lambda_function.py
"""
lambda_function.py
"""
# postリクエストをline bot APIに送るためにrequestsのimport
import os
import time
from datetime import datetime, timezone
import pytz
import urllib.request, urllib.error
from linebot import LineBotApi
from linebot.models import TextSendMessage
import json

CONSUMER_KEY = os.getenv("CONSUMER_KEY")
# line messaging APIのトークン
LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN")

line_name_dict = {
    "odpt.Railway:TokyoMetro.Ginza": "銀座線",
    "odpt.Railway:TokyoMetro.Marunouchi": "丸ノ内線",
    "odpt.Railway:TokyoMetro.Chiyoda": "千代田線",
    "odpt.Railway:TokyoMetro.Tozai": "東西線",
    "odpt.Railway:TokyoMetro.Yurakucho": "有楽町線",
    "odpt.Railway:TokyoMetro.Fukutoshin": "副都心線",
    "odpt.Railway:TokyoMetro.Hanzomon": "半蔵門線",
    "odpt.Railway:TokyoMetro.Hibiya": "日比谷線",
    "odpt.Railway:TokyoMetro.Namboku": "南北線",
}

def lambda_handler(event, context):
    """
    lambda_handler
    """
    print('event: {}'.format(event))
    print('context: {}'.format(context))

    URL = os.getenv("URL")
    response_body = urllib.request.urlopen(url=URL).read()
    data_dict = json.loads(response_body)

    # 現在時刻
    now = datetime.now(tz=timezone.utc)
    tokyo = pytz.timezone('Asia/Tokyo')
    # 東京のローカル時間に変換
    jst_now = tokyo.normalize(now.astimezone(tokyo))
    content0 = jst_now.strftime("%m月%d日 %H:%M現在")

    info_list = []
    for t in range(9):
        line_name = data_dict[t]["odpt:railway"]
        train_info_text = data_dict[t]["odpt:trainInformationText"]["ja"]

        content = []
        # 路線名
        content1 = line_name_dict[line_name]

        # 運行状況（詳細）
        content3 = train_info_text

        # lineに通知するメッセージを組み立て
        content.append("●" + content1)
        content.append(content3)

        info_list.append(content)

    content_text = []
    for i in range(9):
        content_text.append('\n'.join(info_list[i]))

    notification_message = content0 +'\n' + '\n\n'.join(content_text)

    line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)
    line_bot_api.broadcast(TextSendMessage(text=notification_message))

    return {
        'status_code': 200
    }

if __name__ == "__main__":
    print(lambda_handler(event=None, context=None))
```

```requirememts.txt
pytz
line-bot-sdk
```

### 2.1.1.APIの呼び出し

- 該当箇所は以下の通りです。URLは環境変数として設定しています。
APIを使うことでコードも大変スッキリとしました。

```Python
import urllib.request, urllib.error

    URL = os.getenv("URL")
    response_body = urllib.request.urlopen(url=URL).read()
    data_dict = json.loads(response_body)
```

### 2.1.2.LINEへの通知

- 事前にLINE公式アカンウントにて トークンと、API key の取得が必要です。
　発行されたトークンとAPI keyは大切に保管してください。
    - 応答設定：Bot
    - Webhook：On　...LINE Messaging APIを利用します
- ソースの該当箇所は以下の通りです。
　こちらもコードはAPIの利用方法に倣うだけです。

```python
from linebot import LineBotApi
from linebot.models import TextSendMessage

CONSUMER_KEY = os.getenv("CONSUMER_KEY")
# line messaging APIのトークン
LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN")

    line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)
    line_bot_api.broadcast(TextSendMessage(text=notification_message))
```

# 3.Lambdaへアップロードするためのzipファイルを作成する

- zipファイルを作成するスクリプトは以下の通りです。

```shell:make_upload.sh
rm upload.zip
rm -r upload/
rm -r download/

mkdir -p download/bin
mkdir upload
cp -r download/bin upload/bin
cp app/lambda_function.py upload/
pip install -r app/requirements.txt -t upload/
cd upload/
zip -r ../upload.zip --exclude=__pycache__/* .
cd ../

rm -r upload/
rm -r download/
```

# 4.Lambda関数の環境変数を設定する

- Lambda関数の環境変数セクションを表示して、下記設定を行ないます。

| キー | 値 | 備考 | 
| :--- | :--- | :--- | 
| CONSUMER_KEY | (64文字) | | 
| LINE_ACCESS_TOKEN | (173文字) | | 
| URL| https://api.odpt.org/api/v4/odpt:TrainInformation?odpt:operator=odpt.Operator:TokyoMetro&acl:consumerKey={CUSTOMERKEY} | 列車運行情報を取得します。customerKeyの設定が必須。 | 

# 5.実行結果

![IMG_1791.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/85191436-a734-23db-d5f8-bd0182f44207.png)

---

#### (編集後記)
（お願い）APIを利用できる情報提供者からは、必ずAPIを使いましょう。

この記事ではいろいろと端折っている部分がありますので、細かい手順などは[元の記事](https://qiita.com/_whitecat_22/items/1fc804847dce27d5fb94)を参考にして頂ければ幸いです。

天気予報、株価の設定値到達アラート、服薬の飲み忘れ防止通知、等々に応用が利くことですので、みなさんのアイディアで様々に利用してみてください。
現在は定期実行していますが、LINE公式アカウントではLIFFアプリをつくることで、情報が欲しいタイミングでLINEの画面上からボタンひとつでAPIを呼び出すことも可能となります。こちらの機能もぜひ試してみてください。