Webサイトのメンテナンス情報を定期的にslack送信する


Webサイト情報を取得して「XXXX」というワードが含まれる場合は、Slackへ通知を行うという仕組みを作成しました。  
Webサイトのメンテナンス情報などを取得できる他、推しの情報や新着情報の通知の自動化なども可能かと思います。  

:::note alert
スクレイピングは、利用方法によっては、法律で罰せられる可能性もございます。
対象のサーバへの過度なアクセスや、著作権と言った部分等には十分ご注意ください。
:::

主な構成は以下です。  

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/372fd337-e460-7e17-7026-b8d97a964405.png" width=600 />  


# ローカルでの稼働確認
Webサイトへのリクエストには`requests`、スクレイピングには`Beautiful Soup`を利用しました。  
Lambdaで当該ライブラリを利用する場合は、zip化してLambda Layerに載せると言った作業も発生するのでご注意ください。  

ひとまずローカルで確認してみます。  
### ライブラリのインストール  
```
$ pip3 install beautifulsoup4
$ pip3 install requests
```

### スクレイピングの作成
流れとしては、Webサイトにアクセスし、HTML情報を取得し、テキスト情報のみの状態にして検索といった簡単な形となっています。  
BeautifulSoupに関しては公式がありますので、そちらで詳しい情報をご覧ください。(https://www.crummy.com/software/BeautifulSoup/bs4/doc/#get-text)

スクレイピングしたコードの中に、自分が検索したいワードが存在した場合にSlack通知を行う仕組みとなっています。  
`search_words`に`映画化`や`第二期`とかを書いておけば、推しの情報の検知にも利用できるかなと思います。  

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import json
from bs4 import BeautifulSoup

url = '検索したいWebサイト'
res = requests.get(url)

# HTMLの要素情報取得
soup = BeautifulSoup(res.content, 'html.parser')

# タグの情報や空白などを抜き、テキストのみの状態にする
soup_text = soup.get_text(strip=True) 

# 検索ワードの設定
search_words = ['メンテナンス','一時停止','迷惑']

# フラグの初期化
word_flag = False

for word in search_words:
    # 文章の検索
    if word in soup_text:
        # 存在する場合は、フラグを立ててfor文を抜ける
        word_flag = True
        break
```

### Incoming Webhookの導入
Slackへの通知は、Incoming Webhookを利用します。  
post形式でソースコード上からメッセージを送信できるようになります。  

1. Slackのホーム画面へ移動 
![スクリーンショット 2022-04-16 12.30.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/64825b9f-99a3-7f1b-beff-b95038e0b0bc.png)

2. ホームの一番下にある「App ディレクトリを表示」を押す
![スクリーンショット 2022-04-16 12.30.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/eb03b57b-f328-76ba-c7b8-d41e2fbc335f.png)

3. 検索窓より、「Incoming Webhook」を検索する
![スクリーンショット 2022-04-16 12.30.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/4a717c05-97ff-dfd5-7a52-f47b08b9e834.png)

4. 「Slackに追加」を押す
![スクリーンショット 2022-04-16 12.31.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/fb5fb807-61dd-dbb4-81a9-16a47b7ce48d.png)

5. 任意のチャンネルを選択する
![スクリーンショット 2022-04-16 12.31.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/a51a486e-8915-13d5-1523-c3d5ede5c471.png)

6. 「Incoming Webhook インテグレーションの追加」を押す
![スクリーンショット 2022-04-16 12.31.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/1bf7ce07-363e-3603-0492-5235c62dd2e4.png)

7. Webhook URLが作成される  
![スクリーンショット 2022-04-17 11.25.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/8a7d8aa8-1f1c-8e76-d299-353132e347c2.png)

8. Slack側の確認
チャンネルを確認すると追加が確認できます。
![スクリーンショット 2022-04-17 11.28.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/4fb5fa2c-0bd8-b2b3-a834-e4f2f4e0fcab.png)


Slack通知をするための準備は完了しました。  
あとは、`requests.post`を利用することで、送信が可能となります。  

### Slack通知のソース反映  

Slackへ通知を行うユーザーやアイコンは自分で設定できるため、見やすくて分かりやすいですね。  

そこまで深く読んでいませんが、Slackで利用できる絵文字の英語の綴りを入れることでアイコンが設定できるのかなと思っています。  
絵文字はこちらを参考にさせていただきました。(https://qiita.com/yamadashy/items/ae673f2bae8f1525b6af)

#### 反映後のソースコード  
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import json
from bs4 import BeautifulSoup

url = '検索したいWebサイト'
res = requests.get(url)

# HTMLの要素情報取得
soup = BeautifulSoup(res.content, 'html.parser')

# タグの情報や空白などを抜き、テキストのみの状態にする
soup_text = soup.get_text(strip=True) 

# 検索ワードの設定
search_words = ['メンテナンス','一時停止','迷惑']

# フラグの初期化
word_flag = False

for word in search_words:
    # 文章の検索
    if word in soup_text:
        # 存在する場合は、フラグを立ててfor文を抜ける
        word_flag = True
        break

if word_flag:
    # メッセージの作成
    send_message = '検索ワードが見つかりました。\n XXXXXXXXX を確認してください'
    # Slackへの送信準備
    slack_url = '作成したWebhook URL'
    send_json = json.dumps({
        'text' : send_message, # 送信したいテキスト文章
        'username' : 'Slack通知くん', # ユーザー名の設定
        "icon_emoji" : ':postbox:' # アイコンの設定
    })
    # Slackへの送信
    requests.post(slack_url,data=send_json)
```

実行してみるといい感じです。  
![スクリーンショット 2022-04-17 11.45.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/90e81e11-75bb-7c8c-431b-592a19e85504.png)


# Lambdaへの反映
定時起動で送信などができるようにしたいため、AWSを利用します。  
トリガーの設定とかが楽なので、Lambda + EventBridgeを利用します。  

1. 適当にLambdaを作成
![スクリーンショット 2022-04-17 11.49.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/0d16f438-a708-35a6-2cd8-f1cfcc428f24.png)

2. ローカルのソースコードを反映
Lambdaでは、`lambda_handler()`が一番最初に呼び出される関数のため、ローカルのソースを`lambda_handler()`へ反映します。  
また、`Deploy`を押さないと反映は実行されないためご注意ください。  
![スクリーンショット 2022-04-17 11.52.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/038aa4f6-f413-208d-79af-84a7419a25e7.png)

3. テストしてみる
落ちます。    
理由としては、`requests`や`BeautifulSoup`のライブラリ情報が存在しないからです。  
https://sebenkyo.com/2021/05/21/post-1979/ を参考にLambda Layerにライブラリを追加します。  

### Lambda Layerの追加
1. ローカルにて以下を実行
   ```
   $ mkdir python
   $ pip3 install -t python requests
   $ pip3 install -t python beautifulsoup4
   $ zip -r9 layer.zip python
   ```  


2. カレントディレクトリにzipファイルが作成されるため、Lambda Layerに追加する
Lambda > レイヤー > Layerの作成から作成します。  
ローカルのzipファイルをアップロードします。  
作成後は「バージョン ARN」が作成されるため控えておきましょう。  
![スクリーンショット 2022-04-17 12.05.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/b4fecae9-10cc-d898-6ad0-06a5e450464a.png)

3. 作成したLambdaにLambda Layerの反映
Lambda関数の一番下に行くとLayerの追加が存在するため、押します  
![スクリーンショット 2022-04-17 12.10.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/989c7e44-ab02-6c75-a5c0-de1481f5dc1b.png)

4. 「ARNを指定」で反映  
作成したLambda LayerのARNを入力します。  
![スクリーンショット 2022-04-17 12.10.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/872edcbd-b5e4-61df-5726-c1e84a6dbacf.png)  

5. Deployを押して、テストする  
成功しました。    
【Lambda】
![スクリーンショット 2022-04-17 12.15.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/376d5ab8-c9f2-2132-45e6-393281021beb.png)
【Slack】
![スクリーンショット 2022-04-17 12.14.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/937ef4cd-4fdd-fdde-d5e4-103fee264a04.png)  

あと少しです。

# EventBridgeの導入
EventBridgeを利用することで、任意の時間に起動するようなトリガー設定が可能となります。  

1. トリガーの設定を選択 
![スクリーンショット 2022-04-17 12.22.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/b91bcefa-48f6-c85f-1ce5-f3cf16685780.png)

2. EventBridgeを選択し、ルールを設定
今回はcron式を利用して、すべての日に、5分に1度トリガーを設定するような設定を行なっています。  
cron式の詳細はネットで調べてみてください。  
![スクリーンショット 2022-04-17 12.51.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/f4e0aa37-0648-c942-3ba1-78cfc1b98a4f.png)  

3. 反映されると以下のような形になる。  
※Deployボタンは押さなくても反映されました。  
![スクリーンショット 2022-04-17 12.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/bf79c94f-c466-bdc9-9f6f-23543a2e2a5e.png)

4. 検証のため少し待つ。  
洗い物している間に通知が来ていました。  
![スクリーンショット 2022-04-17 12.44.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412976/27e571bb-03ad-5042-a245-dd8ad930ec6a.png)


# 最後に
今回は手取り早く確認を行うため、Lambdaへの直書きおよび、マネージメントコンソール上での操作を行いました。  
CI/CDやリポジトリ管理を意識するのであれば、Serverless FrameworkやAWS SAMを利用したデプロイなども効果的かと思います。  

また違ったアプローチの仕方だと、Step Functionsを利用したバッチ処理なども一つの手かと思います。  

## 余談
本日は情報処理試験ですね。  
参加されている方は、頑張ってください。