LINE BOT で ウェブページの更新監視する

## tl;dr
* 完全に個人用であれば、かんたんに LINE の Bot を作成することができました。
* Broadcast(友達登録した全員に送信)であれば サーバーも不要で動かせます。
* また、ngrokなどを活用することで、特定少数のグループに投稿することもサーバー不要で可能でした

## はじめに
* 限定商品の販売ページのようにウェブページを定期的に確認しに行きたいときがあると思います。
* ここでは Python で 定期的な更新監視と LINE BOT による通知をするコードを書いてみました。

## 流れ
1. LINE Developers に登録
2. Channel access token を手に入れる
3. LINE 上で bot と友だちになる
3. [line/line-bot-sdk-python](https://github.com/line/line-bot-sdk-python) を使ってLINE BOTを書く
4. [psf/requests-html](https://github.com/psf/requests-html) を使って更新監視コードを書く

* なお、特定ユーザーや特定グループに投稿したいなら投稿先を示すIdを手に入れる必要があります。
   * Webhook で BOTに対するイベントを受ける必要があるのでWebサーバーが必要です
   * もし準備が難しい場合は ngrok などを使えば とりあえず必要十分です。
     * [ngrok と Python で 単に webhook を受け取ってみる](https://qiita.com/mmurasawa/items/e508a6b04b5f15f155f9)

## 説明
### LINE Developers に登録
* [LINE Developers - Messaging API を利用するには](https://developers.line.biz/ja/docs/messaging-api/getting-started/)
* 公式を見るのが一番ですね。UIが少し変わってたりするようなので留意ください
* ログインにはLINEのIDが必要です。チャネルの作成まで行なってください。
    * なお、作成するチャネルは Messaging API channelです。

|チャネル選択|
|:-:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/f7718d47-31ea-5f00-f360-b02e8a351f37.png" />|


### Channel access token を手に入れる
* チャネル作成後 Messaging API タブ から Issue(申請) をして ``` Channel access token (long-lived)``` を取得します。

| channel access token|
|:-:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/387bb635-0d30-7dd1-9eeb-726ed95106fb.png">|

### 友達登録する
* channel access token を取得した この画面上部のQR コードを読み取って LINE で友だちになってください。

### [line/line-bot-sdk-python](https://github.com/line/line-bot-sdk-python) を使ってコードを書く
* LINE が公式で python 用に SDK を公開しているのでこれを使います。

#### SDKをインストール 

```
$ pip install line-bot-sdk
```

#### 友達全員と、先程取得したグループ宛にそれぞれ投稿してみます
* broadcast が 友達登録全員に対しての送信になります。

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

access_token = 'XXXXXXXXXXXXXXX'

line_bot_api = LineBotApi(access_token)
line_bot_api.broadcast(TextSendMessage(text='友達全員にBroadcast'))
```

#### 動作確認
* 以下のように「友達全員にBroadcast」というメッセージが BOT から送られてきているはずです。
 * うまく行かない場合はaccess_token, 友達登録等見直してみてください。

|メッセージ例|
|:-:|
|![1578397220270.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/74e41c0d-5860-cf04-a3e8-af7d523757ea.jpeg)|


### 4. [psf/requests-html](https://github.com/psf/requests-html) を使って更新監視コードを書く
* Webページの監視をするためには、表示内容を解釈する必要があります。
* ここでは  [psf/requests-html](https://github.com/psf/requests-html) を使ってみます。

#### インストール
```
$ pip install requests-html
```

#### 使い方
##### 説明
* 例として 以下のようなページ の 商品が注文できるかどうかを見てみます。
 * このWebページでは 注文できるかどうか以下のように表現されています。
 * 「ご注文できない商品」の場所が「在庫あり」などに変化するので、これを見張れば良さそうです。

```html
<div class="status-text">
	<div class="status-heading">
		<span class="status">ご注文できない商品*</span>
	</div>
	<div class="status-note">
	<p>

	</p>
	</div><!-- /div#status-note -->
</div>
``` 

##### スクレイピングしてみる
* 以下のように Webページにアクセスしたあと

```python
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('https://XXXXXXXXXXXXX/') # <---- ここにアクセスしたいURLを入力
```

* r.html.find() を利用して 要素を絞り込むことができます。

```python
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('hxxps://XXXXXXXXXXXXX/') # <---- ここにアクセスしたいURLを入力
r.html.find('div.status-heading span.status')[0].text # <----- ご注文できない商品*
```
* うまく 文字列を得られたので あとはこれで条件判定してあげれば良さそうです。

#### LINEの通知部分と組み合わせてみる 
* 品切れを示す文字列が 消えたら 通知を送るようにしてみます。

```python
# -*- coding:utf-8 -*-
from linebot import LineBotApi
from linebot.models import TextSendMessage
from requests_html import HTMLSession

ACCESS_TOKEN = 'XXXXXXXXXXX'
TARGET_URL = 'hxxps://XXXXXXXXXXXXX/rb/16057071/'
STATUS_CSS_SELECTOR = 'div.status-heading span.status'
NG_STATUS = 'ご注文できない商品'

def get_status():
    session = HTMLSession()
    r = session.get(TARGET_URL)
    return r.html.find(STATUS_CSS_SELECTOR)[0].text

def broadcast_to_friends(message):
    line_bot_api = LineBotApi(ACCESS_TOKEN)
    line_bot_api.broadcast(TextSendMessage(text=message))

if not NG_STATUS in get_status():
    broadcast_to_friends("商品が購入できます:" + TARGET_URL)
``` 
* 実行すると以下のようにメッセージが送られてきました。

|メッセージ例|
|:-:|:
|![1578400246124.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/2cd3afa2-341b-79a7-e48e-8b2564675c3c.jpeg)|

### 特定ユーザーや特定グループに投稿したいとき
* broadcast ではなく push message になります。
    * https://developers.line.biz/en/reference/messaging-api/#send-push-message 
* これを送信するためには group などを特定するIDが必要です。
* IDは BOTに対する操作が行われたときに webhook で LINE から送られてくるので、その準備をします。

#### webhook を受け取る
  * [ngrok と Python で 単に webhook を受け取ってみる](https://qiita.com/mmurasawa/items/e508a6b04b5f15f155f9) などを参考に webhook を受け取れる環境を整えてください。
  * 準備ができたらそのURLを Messaging API タブの Webhook URL に入力
  * Verify を押して success となったら Use Webhook にチェックを入れておきます。

|設定例|
|:-:|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/b5beaf54-54f4-e4b1-4495-885ba0d48eda.png)|

#### グループにBOTを入れて group_id を得る
* 次に、 Messaging API タブの  ```Allow bot to join group chats``` の設定を Enabled にします。

|設定例|
|:-:|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32855/eb6312db-a053-e6cc-6f10-3e74cf8cf369.png)|

* その状態で 適当なグループに この LINE BOTを入れると webhook が飛んできます。
    * ngrok には リクエストを確認できる画面があるので、以下のように確認できます。
        * 必要なのは events[0].source.groupId だけです。

|ngrokのinspect画面で確認している例|
|:--:|
|![Screenshot_2020-01-03 ngrok - Inspect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15633/1d46fa93-c059-33de-b7d6-557f4b11ba24.png)|

#### broadcast ではなく push_message へ
* groupId が取得できたら```broadcast``` ではなく ```push_message``` にします。
    * https://github.com/line/line-bot-sdk-python#push_messageself-to-messages-notification_disabledfalse-timeoutnone 
* このとき第一引数に id を与えてください。

```diff
-line_bot_api.broadcast(TextSendMessage(text='友達全員にBroadcast'))
+line_bot_api.push_message(group_id, TextSendMessage(text='ある個人にpush message'))
```

### 最後に
* あとはタスクスケジューラや cron などで定期的に実行するようにすれば完成です。
* なお、このように機械的にwebページへアクセスすることは場合によってはアクセス先のサーバーに負荷をかけることにつながるため、実際に運用する場合は 下記ページを一読することをおすすめします。
* http://librahack.jp/
* https://vaaaaaanquish.hatenablog.com/entry/2017/12/01/064227