Raspberry Pi公式ブログに紹介されていた「Build a Raspberry Pi Zero W Amazon price tracker」を試してみることにします。
これはAmazonにある特定の商品の価格が変更(値下げ)されたらメールで通知するPythonコードの紹介でした。作者は動画で解説しており、意外と分かり易かったです。
こういった商品の価格変動を知るサイトとして価格コムが有名ですが、できればAmazonで販売されている商品だけの価格変動を知りたい、と思うことってありますよね。私はあります。
公式ブログでは動画が紹介されているだけでした。ここでも更にそれを紹介するだけでは意味はありませんから、動画を観て実際にPythonで作ってみました。
これ、別にRaspberry Pi で実行する必要性は無いのですけど、常時接続されている端末という意味でRaspberry Pi が適任です。
今回はこれ専用というわけではなく、他にも色々と稼働させているRaspberry Pi で行おうかと思っています。そしてメールで知らせる形で紹介されていますが、そんなに緊急性もありませんから、自宅Wi-Fi内で充分で他の形はないなかーと考えています。
ハマった点なども踏まえ、試行錯誤した情報をご紹介しますので、同じようにトライしてみてください。
追記:メール送信の情報を更新しました。
学習のため
Amazonの場合は、APIがあります。
今回のようなスクレイピングは、サイトによっては禁止されています。あくまでもプログラミングの勉強のために、一時的な利用に留めておきましょう。
取りあえず写してみる
私はエンジニアでもないため、とにかく動画を観て同じようにコードを写してみました。動画は意外と分かり易く解説してくれています。
ただ、作者のサイトのリンクを訪れるとChromeではセキュリティエラーが出たので見ていません。(何でだろう?怪しすぎる・・・)
また、作者の動画内で使っている環境は、macOSで「IntelliJ IDEA」というIDEを使っていますね。
今回はRaspbianでも使っている「Thonny」のmacOS版で書いてみました。ThonnyもIntelliJ IDEAも各プラットフォームで動作するので助かります。(OSS万歳!)
取りあえず写経です。
環境でハマる
先ずRaspberry Pi 関係無く、macOSでThonnyを使ってみた状態でハマりました・・・。いや、正確にはmacOSのPython3の仮想環境ですね。
ちょっと上手くまとめられないのでかいつまんで書くと、
そもそもPython2とPython3の環境が同居しているのが問題の切り分けを複雑にしています。更に、catalinaにアップデートしてから通常通りに必要なライブラリがインストールすらできない始末でした。
結果的にはpyenvの環境でPython3を動かすのにアレコレと対応してやっとできました。手順も正確には覚えていません。
Pythonのバージョンを調べ、デフォルトをPython3に登録、そしてPythonのパスを調べたら、何故かroot権限になっていたので、それをユーザーへ変更して解決しました。
次のようなコマンドがヒントになればと思います。
python -V
pyenv global install 3.7.X
which python
sudo chown USERNAME /Users/(YourUserName)/.pyenv/shims/
ここまではmacOSでの話なので、RaspbianならそのままThonnyで動画のコードを写経してください。
そしてその後、Thonnyの環境は以下でOKでした。(macOS Catalina)

日本のAmazon環境でハマる
動画はソースコードがキチンと表示されていますので、一時停止して写せます。私は順番に動画を再生しながら、作者と同じように追記したり編集したりを真似したので、より一層理解が深まりました。オススメです。
最終的にはRaspbianでも動かすのですが、取りあえず動作させるためにコードは少し変更しました。
price = soup.find(id="priceblock_ourprice").get_text().strip()
価格を取ってくるセレクタ「priceblock_ourprice」の中身は、海外だと通貨単位+数字の中で、小数点が入っています。
$150.00
一方で日本の場合は、通貨単位+数字の間に桁表示のカンマが入っています。
¥11,999
これだと通貨表示とカンマを取らないと演算できません。
diff = price - WANTED_PRICEの部分です。
diff = price - WANTED_PRICE
if price > WANTED_PRICE:
print(f"設定金額よりまだ {diff}円高い。")
エンジニアでない私は、試行錯誤した結果、Pythonのreを使い数字以外の文字を削除することで対応しました。
price = int(re.sub("\\D","",getPrice()))
動画のコードにあった[1:4]を上手く当て嵌めればいいのかな??ちょっと分かりませんでした・・・。
結果としてはOKで、以下のように差額(動画での{diff})と現在価格を表示させます。
設定金額より安くなった!差額は -1円の11999円です。
ここまでの機能としては、以前に試した「ラズパイでWEBスクレイピングーjupyter notebook」と同じです。
WEBサイトの情報を取得して、その情報を元に分岐処理で希望の処理である「価格安い場合」にメールを送信するという形です。
動画とコードが違う箇所は、この文字列の取り扱いくらいで、他はそのままでもOKでした。
どのように通知するか?
動画の中盤からメールでの送信の機能を盛り込んでいます。gmailのsmtpサーバーを使っていますね。ここは素直にメールで・・・といきたいところですが、あまりメールが届くのも五月蠅そうなので、こちらから任意で取得する程度なら、このメール送信機能は省いても構わないと思いますよ。
あとは、指定するURLを任意で簡単に変更できたり、追加できたら便利かも知れません。
少なくても例えばRaspberry Pi の価格だけなら、いくつかを事前に登録しておくことで済むでしょう。
実際に狙っていた商品が値下げされたとしても、毎回購入するわけでもありませんから!
価格の遷移を知りたいということなら、それらを保存できる仕組みにして、グラフ化できると面白いかも知れません。
これらをスマホなどの端末から取得に行くようにするか、Raspberry Pi なので、人工音声で喋らせるのもアリです。(突然だとビックリするけど!)
アレクサとの連携だと文系の非エンジニアには難しいですが、Raspberry Pi にボタンなどで切っ掛けを与えてあげられれば、それの時だけ取得に行く(実行させる)というのもありです。
他にも、小さなLCDディスプレイでも、大きなHDMIディスプレイでも、表示だけさせるという手もあります。決まった時間で取得に行き、現在価格を表示を表示させる。または価格が変動した時だけ色で知らせるなど、出来る範囲の仕組みで楽しめそうです。
スマートミラーに表示してあげるとかどうでしょう?
それだと朝から気になって仕方ないかな・・・。
メール部分も実装して試してみた
実際に写経してみて分かったことがあります。動画の説明は理解しやすいけれど、やはり端折っている部分が多いので、自分で試してみないと修正が難しいです。
特にメール送信部分はgmailのsmtpサーバーを使いますが、現在だと2段階認証とかスマホ認証とかセキュリティが高い設定が一般的なので、通常はブロックされてしまいます。

ただ、gmail関係はネットに情報が多いからまだマシです。
私のgmailのセキュリティ設定はスマホ認証と2段階認証をしていますから、そのままではPythonコードからログインできません。
先にgmailのアカウント設定で、アプリパスワードを設定します。
アプリ パスワードを作成、使用する
Googleアカウントのヘルプ
注: 2 段階認証プロセスを使用していて、Google アカウントにログインしようとすると「パスワードが正しくありません」というエラーが表示される場合、アプリ パスワードによって問題が解決する可能性があります。
Google アカウントに移動します。
左側のナビゲーション パネルで [セキュリティ] を選択します。
[Google へのログイン] パネルで [アプリ パスワード] を選択します。このオプションが表示されない場合は、次のような理由が考えられます。
アカウントで 2 段階認証プロセスが設定されていない
2 段階認証プロセスのセキュリティ キーのみが設定されている
職場や学校などの組織のアカウントを使用している
アカウントで高度な保護機能を有効にしている
下部にある [アプリを選択] を選択し、使用するアプリを選択します。
[デバイスを選択] を選択し、使用するデバイスを選択します。
[生成] を選択します。
画面の手順に沿ってアプリ パスワードを入力します。アプリ パスワードは、デバイスで黄色いバー内に表示されている 16 文字のコードです。
[完了] を選択します。
ほとんどの場合、アプリ パスワードの入力はアプリやデバイスごとに一度のみのため、覚える必要はありません。
設定でパスワードを生成すると、そのパスワードでログインが可能になります。
この辺はgmailの話なので細かくは触れません。
アプリパスワードを生成して利用することで、2段階認証など関係無く使えます。
動画のコードから書き換えたのは以下です。def sendMail():の中身ですね。
try:
server.ehlo()
server.starttls()
server.login(EMAIL_ADDRESS, 'アプリパスワード')
encoding = 'utf-8'
message = MIMEText( mailtext.encode( encoding ), 'plain', _charset=encoding )
message['Subject'] = Header( subject, encoding )
message['From'] = EMAIL_ADDRESS
message['To'] = "送り先メールアドレス"
result = server.sendmail( EMAIL_ADDRESS, '送り先のメールアドレス', message.as_string() )
print("Send Email")
server.quit()
except smtplib.SMTPException as e:
print("エラーが起きました!送信できません")
実際にはエラーもなく動作しました。
しかし、送り先メールアドレスなど変数で定義してあげれば、コードはもっとシンプルになります。動画のコードから大幅に変更すれば可能ですが、あまり大きく異なると自分でも混乱するので、一先ず動作させることにしたので、継ぎはぎだらけですね・・・。
スペルミス以外にエラーで難儀したのが、エンコード形式です。
msg = _fix_eols(msg).encode('ascii')
文字コードのエラー
UnicodeEncodeError: 'ascii' codec can't encode characters in position 14-25: ordinal not in range(128)
実はメール送信だけに限れば、お決まりの方法があるので、それで書いた方が簡単になります。慣れたらテンプレートみたいに作成しておけば、色んな場面で使えますね。自分だけの仕組みならgmailでもアリかなと思います。
結果としてこうなりました。意外と便利かも。

ターミナルではこんなです。

参考:Python3でメール送信(文字コードエラー
参考:smtplib --- SMTP プロトコルクライアント
Pythonでツール作り?
Pythonはプログラム言語の中では比較的に覚えやすい言語です。
仕事でバリバリとはいかなくても、小さなツール作りに最適です。Raspberry Pi に簡単に仕込める点と、開発環境がRaspbianなら最初から用意されている点が利点です。
冒頭のようにmacOSでPython3絡みでハマることも少ないでしょう。基本は何もせずともPythonコードを書いて実行できます。
大きな仕組みやWEBサービスなどは難しいですし、エラーが出ても解決するのに時間がかかります。僅かな行数で「少し便利な仕組み」を作れると、オンオフのどちらでも助かる場面もあるのではないでしょうか。
個人的には、仕事などの業務で使えるツールで活用できたら、面倒な手間も減って便利だよなーと感じました。
今回は動画の写経にプラスαくらいで実現できるので、勉強も兼ねて書籍など他のコードなども丸写しから始めると良いと思いますよ。基本は真似っこですからね。
エラー撲滅のヒント
Pythonに限らずプログラミングでは途中経過を知ると答えが見つかることがあります。(少なくても私は)
プログラミングでは変数を利用しますが、その変数が予期せぬ値になっていることがあっても、思い込みもあってなかなか気が付かないものです。
今回も事ある毎にprint文で中身を確認していました。
print(price)
print(WANTED_PRICE)
統合型(IDE)開発エディッタには変数をウォッチできる機能もあります。各エディッタにより操作は異なりますがあれば利用してみてください。
私は面倒な時はとにかくターミナルやら実行結果を出力させて確認しています。Pythonなら上記のようにprint文はお手軽ですね。
初心者は1つずつ確実に理解していく方が結局は近道だったりします。お試しあれ。
PythonコードでAmazonの商品価格の変動を知る方法の紹介の紹介(?)でした!
非エンジニアでもこれくらいは修正で行けますから、そういう意味ではPythonは簡単なんだろうなーって改めて思いました。