【Lambda】毎朝天気を教えてくれるLINEBot!【サーバーレス】


前提

  • AWSアカウントを持っている
  • AWSの基礎知識
  • Pythonの基礎知識
  • APIの基礎知識

作ったもの

天気を取得し毎朝7時にLINEに通知するbot!
※ 母が「毎朝天気知りたい」と言っていたので作成することにしました。


※ テスト中の画像

技術選定(この記事で学べること)

  • 天気を取得し → OpenWeatherMapのAPI
  • 毎朝7時に → CloudWatch Events + Lambda
  • LINEに通知する → LINE Notify

いざ、実装

実装上、APIキーやアクセストークンの取得が先になるため、説明の順番が前後します。説明の順番は以下の通りです。

  1. OpenWeatherMapのAPIによる天気の取得
  2. LINE NotifyによるLINEへの通知
  3. Lambda関数の作成
  4. Lambda関数の編集
  5. CloudWatch Eventsの設定

天気の取得

OpenWeatherMapのAPIの利用には、OpenWeatherMapに登録することで得られるAPIキーが必要!

  1. ここから登録
  2. サインインしてAPI keysタブでAPIキーを確認
  3. あとで必要となるのでコピーしておく

参考:無料お天気APIで簡単スクレイピング

LINEへの通知

LINE Notifyの利用には、LINE Notifyにログインすることで得られるアクセストークンが必要!

また、LINE Notifyと友達になっておく必要があります。
(このLINE Notifyからメッセージが送られてくる。)

  1. LINE Notifyにログインしマイページへ
  2. ページ下部のトークンを発行するからトークンを発行
  3. あとで必要となるのでコピーしておく

参考:PythonでLINEにメッセージを送る

Lambda関数の作成

AWSマネジメントコンソールからLambdaを開き、 関数の作成一から作成
以下のように設定し、関数の作成

  • 関数名:weather_forecast_LINE_bot
  • ランタイム:Python 3.8
  • アクセス権限:AWS ポリシーテンプレートから新しいロールを作成
    • ロール名:lambdaLogRole
    • ポリシーテンプレート:基本的な Lambda@Edge のアクセス権限

基本的な Lambda@Edge のアクセス権限にはCloudWatchへのアクセス権限が含まれていて、これをアタッチしておくことでCloudWatchにログを記録できるようになります。(例えばprint等の標準出力はCloudWatchにログとして記録されます。)

参考:API Gateway + LambdaでREST API開発を体験しよう [10分で完成編]/Lambda関数の作成

Lambda関数の編集

説明上、先にソースコードをお見せします。ソースコードは以下の通りです。

ソースコード
lambda_function.py
from bs4 import BeautifulSoup
import datetime as dt
import requests


def send_line_a_(message):
    URL = "https://notify-api.line.me/api/notify"
    ACCESS_TOKEN = "<コピーしておいたあなた自身のアクセストークン>"
    HEADERS = {"Authorization": "Bearer " + ACCESS_TOKEN}
    requests.post(
        URL,
        headers=HEADERS,
        params={"message": message},
    )


def get_weather_in_(region):
    CORE_URL = "http://api.openweathermap.org/data/2.5/weather"
    REGION = f"?q={region},jp"
    CUSTOM = "&units=metric&lang=ja&mode=xml"
    API_KEY = "&appid=<コピーしておいたあなた自身のAPIキー>"
    URL = CORE_URL + REGION + CUSTOM + API_KEY
    response = requests.get(URL)
    return response.text


def utc2jst_and_parse(lastupdate):
    timestamp_utc = lastupdate[:10] + " " + lastupdate[11:]

    datetime_utc = dt.datetime.strptime(timestamp_utc + "+0000", "%Y-%m-%d %H:%M:%S%z")
    datetime_jst = datetime_utc.astimezone(dt.timezone(dt.timedelta(hours=+9)))
    timestamp_jst = dt.datetime.strftime(datetime_jst, '%Y-%m-%d %H:%M:%S')

    year = timestamp_jst[:4] + "年"
    month = timestamp_jst[5:7] + "月"
    day = timestamp_jst[8:10] + "日"
    hour = timestamp_jst[11:13] + "時"
    minute = timestamp_jst[14:16] + "分"

    return year + month + day + " " + hour + minute


def parse_of_(weather_data):
    soup = BeautifulSoup(weather_data, "html.parser")

    city_name = soup.find("city").get("name")
    min_temp = soup.find("temperature").get("min")
    max_temp = soup.find("temperature").get("max")
    clouds = soup.find("clouds").get("name")
    weather = soup.find("weather").get("value")
    lastupdate = soup.find("lastupdate").get("value")
    date = utc2jst_and_parse(lastupdate)

    message1 = f"\n{date}\n現在の{city_name}の気象情報を\nお知らせします。"
    message2 = f"\n最低気温: {min_temp}\n最高気温: {max_temp}度"
    message3 = f"\n空模様: {clouds}\n天気: {weather}"
    return [message1, message2, message3]


def lambda_handler(event, context):
    weather_data = get_weather_in_("<気象情報を知りたい場所>")
    messages = parse_of_(weather_data)
    for message in messages:
        send_line_a_(message)
    return {"statusCode": 200, "body": {}}

※ <気象情報を知りたい場所>は「 http://bulk.openweathermap.org/sample/city.list.json.gz 」からDLできるJSONに記載がある場所ならどこでも大丈夫です。(世界各地の天気を知れるなんてすごいAPIだなぁ)

さて、Lambdaでは通常、外部ライブラリ(今回でいうbs4requests)のimportはできません。標準ライブラリ(今回でいうdatetime)のimportは問題なく行えます。

Lambdaで外部ライブラリを使用したい場合、外部ライブラリを実行ファイル(lambda_function.py)と一緒にzipして、アクション ▼から.zip ファイルをアップロードをクリックしてアップロードする必要があります。

for_upload
    ┣━ bs4
    ┣━ certifi
    ┣━ chardet
    ┣━ idna
    ┣━ urllib3
    ┣━ requests
    ┣━ soupsieve
    ┗━ lambda_function.py

とにかくLambda上でこのようなディレクトリ構造になっていればOKです。

参考:【Python】AWS Lambdaで外部モジュールを使用する

CloudWatch Eventsの設定

AWSマネジメントコンソールからCloudWatchを開き、 左ペインのイベント今すぐ始めるから以下のように設定し、詳細の設定

  • イベントソース:スケジュールでCron式は0 22 * * ? *と設定
  • ターゲット:+ ターゲットの追加 *から先ほど作ったLambda関数(weather_forecast_LINE_bot)を指定

遷移先で以下のように設定し、ルールの作成

  • 名前:EveryMorning7
  • 説明:毎日7時にトリガー

参考:Amazon CloudWatch Events で cron 式を使う場合は時差に気をつける/具体例

完成!

これにて実装完了となります、お疲れ様でした!
ボリュームを抑えるために、ところどころ説明を端折っており、少々分かりづらいかも知れません。
ご感想、ご質問等があればぜひコメントをお寄せください😊