【初心者向け】AWS Lambda(Python) + API Gatewayで運行情報をお知らせしてくれるSlackAppを作るハンズオン！

## はじめに
こんにちは！DMM WEBCAMPでメンターをしています@piyorです。

通学・出勤の時に電車が遅延しているかの確認をSlackで出来たら
チームメンバーなどで共有ができて便利ですよね

という訳で、運行状況をPythonでスクレイピングしてその結果をSlackに送信するものを作成しました。

### 対象者
- サーバレス(AWS Lambda)に興味がある方
- Webスクレイピングに興味がある方
- SlackAppに興味がある方  

※今回Pythonのコードについては基本的には説明しません
※スクレイピングとはなに？って方は[こちらの記事](https://www.sejuku.net/blog/51241)などを参考にしてください
※スクレイピングを禁止しているサイトも存在します。[こちらの記事](https://qiita.com/n_oshiumi/items/b4efd1f40ec0a1b77376)などを参考にしましょう

### 作るもの

👇こんな感じに、遅延・運休してる路線を通知してくれます
![スクリーンショット 2020-12-01 10.09.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/0a86b2e1-9748-2dc8-2c66-e92426bbcbc3.png)

👇この画像の部分を引っ張って来て、それをSlackに通知をするという流れになります
![スクリーンショット 2020-12-01 10.02.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/6829447b-3d4c-44a3-8232-c9ae1ce3eb4c.png)


## 目次

| 目次 |
|:-:|
| [構成](#構成)  |
| [Lambda関数作成](#lambda関数作成)  |
| [API Gateway作成](#api-gateway作成)  |
| [SlackApp作成](#slackapp作成) |
| [Lambda関数の環境変数を設定する](#lambda関数の環境変数を設定する) |
| [朝の9時にSlackAppがPOSTしてくれるように設定する](#朝の9時にslackappがpostしてくれるように設定する)|
|[終わりに](#終わりに)|
|[参考](#参考)|

## 構成
構成は、朝の通知以外にもSlackからコマンドを叩いたら結果が帰ってくるようにしました

![Untitled (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/19870a2d-71e3-e22c-4e76-8a5805a7cc27.png)

## Lambda関数作成
Lambdaはサーバレスでコードを実行するためのサービスです
今回は、スクレイピング処理・Slackにスクレイピングの結果を送信するために使用します

### Lambda関数を作成する
**関数名**：好きな関数名
**ランタイム**：Python3.8
他はデフォルト値で大丈夫です

![スクリーンショット 2020-11-26 10.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/55d1b197-cd4d-9462-3b10-d7f4bfabd39b.png)

### 外部ライブラリ・関数コードを用意する
#### 使用ライブラリの説明
- os
    - 標準ライブラリの1つでOS依存の機能使用するためのライブラリです
    - 今回は環境変数を利用するために使用します
- requests
    - HTTP通信のためのライブラリです
    - Webページを取得するために使用します
- bs4 (beautifulsoup4)
    - Webページから情報を取得するためのライブラリです
    - スクレイピングを行うために使用します
- slackweb
    - WebHookでSlackにPOSTするためのライブラリです

#### 外部ライブラリ・関数コードの用意をする
Lambdaで外部ライブラリを使用する場合、別途用意する必要があります
詳細はAWS公式ドキュメントの[Python の AWS Lambda デプロイパッケージ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-package.html#python-package-dependencies)などを参考にしてください

**今回は、使用する外部ライブラリ・関数コードを用意しているので次のコマンドを実行してください**
※githubからクローンし、クローンしたファイル群をzip化するものです

```console:コンソール
$ git clone https://github.com/piyoraik/delay_bot.git
$ cd delay_bot
$ zip -r delay_bot.zip ./
```

#### ※コードだけ見たい方※

```python:lambda_function.py
import requests, bs4, slackweb, os

# URL・トークンの定義
url = "https://transit.yahoo.co.jp/traininfo/area/6/"
slack = slackweb.Slack(url=os.environ['SLACK_URL'])

def lambda_handler(event, context):

    response = {
        'statusCode': 200,
        'isBase64Encoded': False,
        'headers': {'Content-Type': 'application/json'},
        'body': 'done'
    }

    fetch_result = data_fetch(url)

    # 取得先のタイトルを取得
    title = fetch_result.title.get_text()


    # 遅延情報の取得
    delay_info = fetch_result.find(id='mdStatusTroubleLine').find_all('a')
    delay_info_text = ''
    for info in delay_info:
        delay_info_text += "\n" + info.get_text() + "\n" + info.get('href')

    slack_send(slack, title, delay_info_text)

    return response

# slackに情報の送信
def slack_send(slack, title , text):
    attachments = []
    attachment = {
        "color":"#D00000",
        "title": title,
        "text": text,
        "mrkdwn_in": ["text", "pretext"]
    }
    attachments.append(attachment)
    slack.notify(attachments=attachments)

    return

# requestsでデータをスクレイピング   
def data_fetch(url):
    get_url = requests.get(url)
    bs4_obj = bs4.BeautifulSoup(get_url.text, 'html.parser')

    return bs4_obj
```

### Lambdaにアップロードする

**.zipファイル**を選択して、zipファイル化した```delay_bot.zip```を選択しアップロードします
![スクリーンショット 2020-12-01 10.19.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/cc453aa0-53aa-42ff-b1a1-1222c72b7a6d.png)

👇アップロードをしてこのようになっていればOKです
![スクリーンショット 2020-11-26 1.24.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/31425cdd-6ea0-6680-57aa-7f7df9bdacf8.png)

#### ※取得URLの変更を行う場合※
```python:lambda_function.py
url = "https://transit.yahoo.co.jp/traininfo/area/6/"
```
現状では```lambda_function.py```4行目のこの記述により
https://transit.yahoo.co.jp/traininfo/area/6/
このURLの情報を取得しています
例えば、関東エリアの運行情報を取得したい場合は、4行目の記述を以下のように修正します

```python:lambda_function.py
url = "https://transit.yahoo.co.jp/traininfo/area/4/"
```

修正後に**最新としてデプロイ**をクリックし、変更を反映させましょう

## API Gateway作成
API GatewayはAPIの作成を行うサービスです
今回は、API Gatewayで作成したURLを呼び出すと先程作成したLambda関数を実行させるために使用します

### API Gatewayの作成をする
APIタイプは**REST APIで構築**を行います
![スクリーンショット 2020-11-26 11.10.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/f38625be-5e85-f9c6-03f4-7e1455274340.png)

**API名**を入力しましょう、他はデフォルト値で問題ありません
![スクリーンショット 2020-11-26 11.21.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/d11653ab-8061-000a-4d20-2353a71f08b9.png)

### メソッドを作成し設定を行う
アクションから**メソッドの作成**をクリックし、**POST**を選択します
※メソッドについては[こちら](https://qiita.com/fukulingo/items/a9e8d18467fe3e04068e)の記事などを参考にしましょう

選択後、セットアップを行います。
**Lambdaプロキシ結合の使用**と**Lambda関数**を選択しましょう、選択後に保存をクリックします
![スクリーンショット 2020-11-26 11.39.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/5da4c672-33ce-35a5-eae6-157ed1939c04.png)

### APIのデプロイを行う
**アクションからAPIのデプロイ**を選択し**デプロイ**をしましょう

![スクリーンショット 2020-11-30 14.34.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/12a97605-6fba-1168-be4c-86702f67b265.png)


![スクリーンショット 2020-11-26 11.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/a9437109-4b05-6cd9-ce13-9ab30c2cae12.png)

### URLを確認する
**デプロイ**をクリック後、URLが生成されます。
**このURLは後で使用するのでいつでも確認できるようにしておきましょう**

![スクリーンショット 2020-11-26 11.58.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/4571ae19-04ce-41a2-97c2-1cac16d02a1b.png)

ここまでで作成したLambda関数を上のURLから呼び出す設定が完了しました
※今回**POSTメソッド**で作成しているのでブラウザでそのままURLを開いてもLambda関数は実行されません※

## SlackApp作成

### SlackAppを作成する
まず、SlackAppを作成するために下記URLにアクセスします
https://api.slack.com/apps

アクセスしたら**Create New App**をクリックします
![スクリーンショット 2020-11-26 12.49.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/463e2b18-919c-2d78-0faa-d97e97265721.png)

SlackAppを作成するウィンドウが出てくるので、**アプリ名とインストールをするワークスペースを選択**します
選択後、**Create App**をクリックし作成を行いましょう
![スクリーンショット 2020-11-26 12.51.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/1ac5f2d3-2a4e-4eaf-87c1-7b4815720354.png)

### Slash Commandsを作成する
SlackからAPI Gateway経由でLambda関数を呼び出すためにこちらを使用します
**SlashCommands**から**Create New Command**を選択します

![スクリーンショット 2020-11-26 14.06.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/4527644b-93fe-8f63-544d-5cf14f77e99b.png)

Slash Commandsを作成ページに遷移するのでコマンドの作成を行っていきましょう

次のように記述します

- **Command**: 呼び出すコマンドを入力
- **RequestURL**: API Gatewayのアドレスを入力
- **Short Description**: コマンドの説明を入力
- **Usage Hint**: 使い方のヒントを入力

入力が終わったら**save**をクリックし保存をしましょう

![スクリーンショット 2020-11-26 14.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/5f263c49-2812-0bfb-aa45-463517ce2f92.png)

### Incoming Webhooksの設定を行う

Incoming Webhooksのページに遷移し**有効**にしましょう
有効にしたら**Add New Webhook to Workspace**という項目が現れるのでここをクリックします

![スクリーンショット 2020-11-26 22.28.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/910f2bd6-ed6a-eb3f-c12e-cb68fa47675b.png)

クリック後、SlackAppが投稿をするチャンネルを選択する画面が出てくるので選択し許可をします
選択後**Webhook URL**が発行されているので控えておきましょう、後で使用します

### ワークスペースにSlackAppにインストール
一通りの作業が終了したので、ワークスペースにSlackAppをインストールしていきます
黄色に表示されている部分を**reinstall your app**を選択し、インストールを行います

![スクリーンショット 2020-11-26 22.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/9b5043b2-5101-2f95-db22-a0a85a6478af.png)

これでSlackAppの設定が完了です

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

### 環境変数の設定
Lambda関数からSlackに投稿するために環境変数の設定を行います
環境変数については[こちらの記事](https://wa3.i-3-i.info/word11027.html)などを参考にしてください

```lambda_function.py
slack = slackweb.Slack(url=os.environ['SLACK_URL'])
```
PythonではLambda関数5行目の```os.environ['SLACK_URL']```が環境変数の部分になります

**Lambdaの環境変数設定**で設定を行います

次のように記述します
**キー**:「SLACK_URL」と入力
**値**:Webhook URLをペースト

記述が出来たら保存を行います、これで環境変数の設定が完了です
![スクリーンショット 2020-11-27 16.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/ff30ba38-ce6d-ab2e-b8b3-e1314dc7c03c.png)

### 動作確認
ここまで終わったらSLACKからLambda関数を呼び出すための設定が完了です！
実際にSlackから動作を確認してみましょう

Slackから```/command```と入力すると次のように出てくるので選択して投稿してみましょう
![スクリーンショット 2020-11-27 16.31.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/bdb18d90-a175-1858-4c0e-aae219104d87.png)

POST後、このようにSlackAppから投稿されたら成功です！
![スクリーンショット 2020-12-01 10.14.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/1327cd73-52b7-4413-6a55-0c85728173b3.png)

## 朝の9時にSlackAppがPOSTしてくれるように設定する
SlackのSlash Commandから要求することは出来たので毎朝9時に投稿してくれるように設定をしていきます

Lambda関数を開き**トリガーを追加**をクリックします

![スクリーンショット 2020-12-01 10.16.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/0316346d-f61b-7540-aee0-a01bac217b20.png)
クリックをすると、トリガー追加画面に遷移するので**EventBridge(CloudWatch Events)**を選択します
今回はこのEventBridgeという機能を用いて毎朝9時に実行させます

それでは設定していきます、次のように入力しましょう
**ルール**:「新規ルールの作成」を選択
**ルール名**:ルール名を入力
**ルールタイプ**「スケジュール式」を選択
**スケジュール式**:```cron(00 00 * * ? *)```と入力

スケジュール式のcronの設定は、AWSのページにも記載されていますがUTC時間で設定をします
設定は**cron(分 時 日 月 曜日)**という記載方法になります
cronの設定については、AWS公式ドキュメントの[ルールのスケジュール式](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressions)を参考にしてください

入力が出来たら**追加**をクリックしトリガーを有効化しましょう

![スクリーンショット 2020-11-28 1.17.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/bbf6a9fd-9484-9d30-b518-9b49bddccd2f.png)
これで毎朝9時にSlackAppが9時になると遅延情報をお知らせしてくれるようになります！

## 終わりに
お疲れ様でした！
今回のハンズオンで少しでもAWSやPythonに興味を持っていただいたら幸いです！

明日は@yoshimurayoshikiの番です！引き続き楽しんでいってください！

## 参考

[Slackにincoming webhook経由でpythonからメッセージをPOSTする](https://qiita.com/satoshi03/items/14495bf431b1932cb90b)
[Python Webスクレイピング 実践入門](https://qiita.com/Azunyan1111/items/9b3d16428d2bcc7c9406)
[API Gateway + LambdaでREST API開発を体験しよう [10分で完成編]](https://qiita.com/tamura_CD/items/46ba8a2f3bfd5484843f)