開拓馬’s blog

いろいろやる

AWS Lambdaを使ってAmazonコインを安く買う

先日AWS Lambdaを初めて使ったので忘備録を兼ねて書きます

今回書いたコードはここで見れます

Amazonコインの価格変動

1年ぐらい前からHearthstoneというデジタルカードゲームにハマっています。 Hearthstoneは課金でカードパックを買えるのですが、Amazonアプリストア版のAndroidアプリを使用すると、Amazonコインでの支払いが可能で、クレジットカードで直接買うよりより少し安くなります。

そんなAmazonコインですが、実は日々値段が変動しているみたいです。

Amazonコインの価格の変動・推移を調べてみる2018-2 | JJゃもの部屋 \(^o^)/ 破滅に向かって2011

↑の記事を見た感じ、10000コインが8100円のときにまとめ買いしておくとおトクなようです。

Amazonコインが安い日を見逃さないために、今回はpythonでwebスクレイピングを行いコインの価格を自動で追跡するスクリプトを作成しました。また、そのスクリプトAWS Lambdaで自動化しました。

Amazonコインの価格を自動で取得する

PythonAmazonコインの価格を追うスクリプトを書きました。

まず、venvを用いて仮想環境を作成し、必要となるライブラリをインストールします。

$ python3 -m venv amazoncoin
$ cd amazoncoin
$ source bin/activate # bash zsh等の場合
$ source bin/activate.fish # fish shellの場合
(amazoncoin)$ pip install requests beautifulsoup4
(amazoncoin)$ pip freeze > requirements.txt #必要なライブラリを書き出しておく(あとで使う)

10000Amazonコインの販売ページは https://www.amazon.co.jp/dp/B00KQVXDW0/ であり、価格はid=priceblock_ourpricespanタグに記載されているので、以下のようなスクリプトで価格を取得できます。*1

import requests
from bs4 import BeautifulSoup

def find_price():
    coin_url = "https://www.amazon.co.jp/dp/B00KQVXDW0/"

    resp = requests.get(coin_url, timeout=1)
    soup = BeautifulSoup(resp.text, 'html.parser')
    target = soup.findAll('span',{'id':"priceblock_ourprice"})
    return int(target[0].text[2:].replace(',', ''))

print(find_price())#=> 8100など

価格をLine Notifyで通知する

次に、取得した価格をLine Notifyを使って自分のLINEアカウントに通知することにしました。 [参考] https://qiita.com/hiro-abe/items/e42f857bd6b40bc178a3

下準備として、LINE Notifyのwebページでアクセストークンを取得し、環境変数LINE_NOTIFY_TOKENに値を書き入れておきます。

$ export LINE_NOTIFY_TOKEN=XXXXXXXXX # bash zsh等の場合
$ set -x LINE_NOTIFY_TOKEN XXXXXXXXX # fishの場合

そして、以下のスクリプトを実行するとLINEに通知が届きます。

import requests
from bs4 import BeautifulSoup
import os

def lambda_handler(event, context):

    output_url = "https://notify-api.line.me/api/notify"
    token = os.getenv("LINE_NOTIFY_TOKEN", "")
    
    price = find_price()
    headers = {"Authorization" : "Bearer "+ token}
    message =  '現在10000 Amazonコインは' + str(price) + '円です'
    payload = {"message" :  message}
    
    r = requests.post(output_url ,headers = headers ,params=payload)
    
    
    return 'Notify Amazon Coin Price is running'

lambda_handler(None, None) # => LINEの指定したトークルームに通知が来る

これで、script.lambda_handler関数を実行することで、Amazonコインの価格がLINEに届くようになりました。

AWS Lambdaで毎日自動で動かす

上記のスクリプトをASW Lambdaを用いて自動化します。 Lambdaを用いて自動化します。今回は毎朝8時に実行させるようにします。

スクリプトのパッケージ化

最初に作成したスクリプトをzipにまとめます。Lambdaは使用するスクリプトをzip形式でアップロードする方式になっているためです。*2 このとき注意しなければならないのは、標準ライブラリ以外のライブラリも一緒にzipにまとめる必要がある点です。

デプロイ用のディレクトリを作成し、そこに今回のスクリプトとライブラリをすべて設置し、zipにまとめることにします。

mkdir deploy #デプロイ用ディレクトリを作成
cp script.py deploy #今回作ったスクリプトをデプロイ用ディレクトリにコピー
pip3 install -r requirements.txt -t deploy/ #-tオプションでデプロイ用ディレクトリにライブラリをインストール
cd deploy/
zip -r package.zip * #パッケージを作成
mv package.zip ../

これで、作業ディレクトリにpackage.zipが作成されます。 動作確認として、以下のようなコマンドを実行したらスクリプトが正しく動作することを確認してください。

(amazoncoin) $ deactivate #仮想環境が有効になっている場合のみ
$ cd deploy/
$ python3 -c "import script;script.lambda_handler(None, None)"
# > Notify Amazon Coin Price is running
# (LINEに通知が来る)

AWS Lambdaの設定

パッケージが完成したので、早速Lambdaのセッティングをしてみます。

まず、Lambdaの管理ページから新しい関数を作成します。 ロールは「テンプレートから新しいロールを作成」を選択し、「Basic Edge Lambdaアクセス権限」を使用します。他は適当に入力してかまいません。 f:id:pf_siedler:20180415133028p:plain

次に、先程作成したpackage.zipをLambdaにアップロードします。 このとき、ハンドラには[pythonのファイル名].lambda_handlerを指定します。 f:id:pf_siedler:20180415133042p:plain

そして、環境変数の欄にLINE Notifyのアクセストークンを入力します。 f:id:pf_siedler:20180415133023p:plain

最後にタイムアウトの値を少し大きめの値に変更しておきます。*3メモリは最小の128mbで足りるのでデフォルトのままでかまいません。 f:id:pf_siedler:20180415133036p:plain

イベントタイミングの指定

次に通知が来るタイミングを指定します。 トリガーにCloudWatch Eventsを指定します。 ルール名と説明は適当に埋め、ルールタイプを「スケジュール式」、 スケジュール式をcron(0 23 * * ? *)を指定します。

f:id:pf_siedler:20180415133315p:plain

これで毎日グリニッジ標準時の23時、つまり日本時間の8時にスクリプトが実行されるようになりました。

おわりに

今回はpythonを用いてwebスクレイピングと、AWS Lambdaによる自動化を行いました。

今回、個人的につまずいた点は以下のとおりです。

  • ライブラリをzipに含めないとライブラリの関数が使えない点*4
  • イベントのトリガーに何を選べばいいのかわからない点

今後の課題として、価格がn円以下のときのみ通知を流すようにしたり、TwitterやDiscordに自動投稿させたりなんかをやりたいと思っています。

今回のプロジェクトはこちらのリポジトリで管理してあります。よかったらforkしてみてください。また、改善点などあればissueに書いてくださると助かります。

*1:後々、価格がn円以下なら通知するといった条件分岐をしやすくするために結果をint型で返すようにした

*2:一応、webページで直接コードを書く機能もある。zipでうpしたファイルをweb上で微修正するということも可能。

*3:今回のスクリプト実行には4秒ぐらいかかる場合があり、デフォルトの3秒ではタイムアウトが生じる場合がある

*4:もしかしたら、これを自動でやってくれるソフトがあるのかもしれないが見つからなかった