switchが入荷したらLINEで通知するプログラムを作った


入手困難(?)なswitchが入荷したらLINEで通知するプログラムを作った。
ここでは、JoshinとAmazonを対象にする。


#環境
- python 2.7
- 実行用マシン：Raspberry pi 3

#必要なもの
- LINEの開発者アカウント
- pythonスクリプトを動かすマシン

#準備

まず、

```
pip install requests
pip install beautifulsoup4
pip install line-bot-sdk
```

を行い、必要なライブラリを入れる。
Raspberry piで行う場合、先頭に`sudo`を記述しないとエラーが出るかもしれない。

次にLINEの開発者アカウントを用意する。
作り方は以下に詳しく載っている。
[raspi起動時にipをlinebotを使って通知するシステムを作った話](http://qiita.com/Mr_smith/items/0576f4d0d4040db44ea9)

ここで欲しいのは、`Channel Access Token`と`Your userId`である。
これらは、以下のプログラムにある`LINE_ACCESS_TOKEN`と`LINE_USER_ID`にコピペする。

#プログラム

``` python:zaikochecker.py
# coding: UTF-8
import re
import requests
from bs4 import BeautifulSoup
from linebot import LineBotApi
from linebot.models import TextSendMessage
from linebot.exceptions import LineBotApiError

LINE_ACCESS_TOKEN = "LINEのアクセストークンをペースト"
LINE_USER_ID = "LINEのユーザーIDをペースト"
line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)

# UA偽装用
my_header = {
    "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; NP06; rv:11.0) like Gecko"
}

# 商品のURL
joshin_url = [
    ""
]

amazon_url = [
    ""
]

# LINE通知時の文字列
result_str = ""

# Joshin用
result_str = "Joshin\n"
for i in range(len(joshin_url)):
    data = requests.get(joshin_url[i], headers = my_header)
    data.encoding = data.apparent_encoding
    data = data.text
    soup = BeautifulSoup(data, "html.parser")
    try:
        detail = soup.find("form",{"name":"cart_button"}).text.encode("UTF-8")
        print(detail) # デバッグ
        if ("販売" in detail) == False: # 販売休止中ですとなっていなければ在庫あり
            if(i == 0) : result_str += "ネオン在庫あり\n"
            if(i == 1) : result_str += "グレー在庫あり\n"
    except AttributeError:
            print("Error")

# Joshin用LINE通知
if result_str != "Joshin\n":
    try:
        line_bot_api.push_message(LINE_USER_ID, TextSendMessage(text=result_str))
    except LineBotApiError as e:
        print(e)

# Amazon用
result_str = "Amazon\n"
for i in range(len(amazon_url)):
    data = requests.get(amazon_url[i], headers = my_header)
    data.encoding = data.apparent_encoding
    data = data.text
    soup = BeautifulSoup(data, "html.parser")
    detail = soup.find("div",id="merchant-info").find("a").text
    print(detail) # デバッグ
    if ("Amazon" in detail) == True: # Amazon.co.jpが販売なら在庫あり
        if(i == 0) : result_str += "ネオン在庫あり\n"
        if(i == 1) : result_str += "グレー在庫あり\n"

# Amazon用LINE通知
if result_str != "Amazon\n":
    try:
        line_bot_api.push_message(LINE_USER_ID, TextSendMessage(text=result_str))
    except LineBotApiError as e:
        print(e)

```

ゴリ押し記述なので効率が悪く、分かりにくいプログラムかもしれない。
このプログラムは簡単に言うと、対象の商品のURLにアクセスし、注文できる状態なのかどうかを確認するものである。

`joshin_url`、`amazon_url`は商品のURLの配列であり、ここに対象となるURLを記述する。

アクセス時にUA偽装をしないと、Amazonでは弾かれるため、`my_header`内に偽装用UAを記述している。

requestやbeautifulsoupの使い方はググれば出てくるので割愛する。
Joshinであれば`cart_button`内に`販売休止中です`という文字列がある場合、注文できない状態である。
つまり、取得した文字列の中に販売という文字がなければ注文できる状態ということである。
ちなみに、Joshinのみtry exceptで処理しているのは、Joshinだとアクセスしすぎた場合に商品ページがうまく表示されない現象が起こるからである。
(特定IPからある時間内に多数のアクセスがあると注文ボタンが出ないようになっている？)
また、Amazonであれば`merchant-info`のaタグに販売業者が書かれるが、これがAmazon.co.jpが販売である場合は注文できる状態であると判断している。

LINEの通知については以下に詳しく載っている。
[LINE API Reference](https://devdocs.line.me/ja/#push-message)
正しいトークン、IDが入力されていれば動くはず。

実際に通知が来るとこんな感じになる。
![スクリーンショット 2017-03-23 13.41.19.png](https://qiita-image-store.s3.amazonaws.com/0/136855/64031a0e-3e79-d25f-aee3-b3b414a6551b.png)


#プログラムの実行
上記のプログラムを一度だけ実行しても、実行したときの状態しか得られない。
よって、ある時間ごとに在庫を確認するようにしないといけない。
ここでは、cronを使って1分ごとに在庫をチェックするようにする。

cronの使い方については、以下を参考にした。
[決まった時間に処理する](http://make.bcde.jp/raspberry-pi/%E6%B1%BA%E3%81%BE%E3%81%A3%E3%81%9F%E6%99%82%E9%96%93%E3%81%AB%E5%87%A6%E7%90%86%E3%81%99%E3%82%8B/)

1分ごとにチェックするのであれば、

```
*/1 * * * * python /home/hoge/zaikochecker.py
```

とすれば良い。

#まとめ
このプログラムによって、実際に通知が来て無事注文することができた。
ただし、問題点もいくつかある。
1つ目は1分ごとにアクセスするため、相手のサーバーに負荷がかかる&弾かれる恐れがある。
2つ目は1分ごとにチェックするため、在庫がある状態が長く続いた場合、1分おきにLINEの通知が来ることとなる。


ヨドバシやビックカメラ等でもbeautifulsoupの使い方さえ分かれば同様の手順で作れると思う。