【ESP32でAmazon自動注文ボタン】Pythonの自動注文スクリプトをAmazon Lambda上で動作させる

DIY
スポンサーリンク

Amazon自動注文ボタンをESP32で作ろうとしています。
前回の記事ではAWS Lambda関数の事前準備ができたので、今回のブログでAWS Lambda関数の作成、動作確認をします。

●自動購入スクリプト
・Python 3.7
・Selenium 3.141.0
・headless-chromiumのバージョンはv1.0.0-37(chromium 64.0.3282.167)
・chromedriverのバージョンはv2.37(Supports Chrome v64-66)
●AWS Lambda
・Python 3.7

 

目次

 


 

はじめに

本記事は「ESP32でAmazon自動注文ボタン」の作成手順をまとめた連載記事の1つです。

前の手順

【ESP32でAmazon自動注文ボタン】Lambda関数で自動注文スクリプトの準備する
Amazon自動注文ボタンをESP32で作ろうとしています。 本記事はLambda関数で自動購入スクリプトの準備する手順をまとめました。

 

次の手順

【ESP32でAmazon自動注文ボタン】自作ボタンをクリックしたら、自動購入スクリプトが実行されるようにする
Amazon自動注文ボタンをESP32で作ろうとしています。 本記事は自作ボタンをクリックしたら、自動購入スクリプトが実行されるようにする手順をまとめました。

 

手順全体や関連情報まとめ

 

自動注文スクリプトをLambda関数で実行するための方針

前々回ブログ(【ESP32でAmazon自動注文ボタン】自動購入スクリプトを作成する(ローカル環境で))で作成した自動注文スクリプトをLambda関数に書き込んだだけではうまく動作しません。
Lambda関数はランタイムとして標準的な実行環境しか準備されていないため、外部のPythonスクリプトやChromeドライバなどは事前準備する必要があります。
また、Lambda関数上で秘密情報を直接扱うのはAWSベストエフォートとして問題ありそうなので、秘密情報は「AWS Systems Managerのパラメータストア」を利用することにします。

 

そのため、前回ブログでは自動注文スクリプトをLambda関数で実行できるようにするため、以下の事前準備をしました。

【前回ブログのターゲット(AWS Lambda関数の事前準備)】

  • Lambdaレイヤーを作成する
  • AWS Systems Managerのパラメータストアに秘密情報を登録する
  • Lambda関数用のIAMポリシーを作成する

 

本ブログはその続きから以下の設定をします。

【本ブログのターゲット(Lambda関数の作成~動作テスト)】

  • Lambda関数を作成する
  • Lambda関数に自動注文スクリプトを登録する
  • Lambda関数にLambdaレイヤをアタッチする
  • Lambda関数にIAMポリシーをアタッチする
  • Lambda関数の基本設定を変更する
  • 自動購入スクリプトの動作確認

 

以下では、AWS Lambda関数の作成から動作テストする手順をまとめます。

 

Lambda関数を作成する

以下に、AWS Lambda関数を作成する方法をまとめました。

  1. AWSマネージメントコンソールにログインする。
  2. AWS Lambda」にアクセスする。
  3. ダッシュボード画面の右上にある「関数を作成」ボタンをクリックする。
  4. 「関数の作成」画面が表示されるので、以下の設定例を参考に入力し、下部の「関数の作成」ボタンをクリックする。
設定項目設定例
関数の作成 オプション「一から作成」を選択
関数名任意のものを記入 (例:autoPurcaser)
ランタイムPython 3.7
アーキテクチャ「x86_64」にチェック
デフォルトの実行ロールの変更「基本的なLambda アクセス権限で新しいロールを作成」にチェック

 

「関数xxxxxを正常に作成しました。」と表示されれば、Lambda関数の作成はできています。

 

 

 

Lambda関数に自動注文スクリプトを登録する

以下に、Lambda関数に自動注文スクリプトを登録する手順をまとめました。

  1. 前章で作成したLambda関数のページを開く。
  2. 「コード」タブを開き、以下のサンプルコードを入力し、「Deploy」をクリックする。

サンプルコード

import time,json,boto3
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By

AMAZON_TOP_URL = 'https://www.amazon.co.jp/'
ITEM_URL = 'https://www.amazon.co.jp/dp/B01BEPNPF6'

def logPrint(str):
    print("%s : %s"%(datetime.now().strftime("%Y/%m/%d %H:%M:%S"),str))

def lambda_handler(event, context):

    logPrint("### START autoPurcaser ###")

    # SSMから秘密情報を取得
    logPrint("### Get Secret Param from SSM ###")
    try:
        ssm = boto3.client('ssm')
        ssm_response = ssm.get_parameters(
            Names = [
                'AmazonLoginID',
                'AmazonLoginPassWord',
            ],
            WithDecryption=True
        )
        AMAZON_LOGIN_ID = ssm_response['Parameters'][0]['Value']
        AMAZON_LOGIN_PASSWORD = ssm_response['Parameters'][1]['Value']
    except Exception as e:
        logPrint('Failed to get secret param from SSM.')
        logPrint(e)
        exit()
    

    # ブラウザの起動
    logPrint("### Open Top Page ###")
    try:
        options = webdriver.ChromeOptions()
        options.add_argument("--headless")
        options.add_argument("--disable-gpu")
        options.add_argument("--hide-scrollbars")
        options.add_argument("--single-process")
        options.add_argument("--ignore-certificate-errors")
        options.add_argument("--window-size=880x996")
        options.add_argument("--no-sandbox")
        options.add_argument("--homedir=/tmp")
        options.binary_location = "/opt/headless/headless-chromium"
        browser = webdriver.Chrome(
            "/opt/headless/chromedriver",
            options=options
        )
        browser.get(AMAZON_TOP_URL)
        time.sleep(1)
    except Exception as e:
        logPrint('Failed to open browser.')
        logPrint(e)
        exit()

    # ログイン
    logPrint("### Login ###")
    try:
        # ログインが必要かチェック
        if len(browser.find_elements(By.ID, 'nav-greeting-name')) > 0:
            logPrint("Already logged in.")
        else:
            logPrint("ID:{0}, num:{1}".format("nav-link-accountList", len(browser.find_elements(By.ID, 'nav-link-accountList'))))
            browser.find_element(By.ID, 'nav-link-accountList').click()
            time.sleep(1)
            logPrint("ID:{0}, num:{1}".format("ap_email", len(browser.find_elements(By.ID, 'ap_email'))))
            browser.find_element(By.ID, 'ap_email').send_keys(AMAZON_LOGIN_ID)
            logPrint("ID:{0}, num:{1}".format("continue", len(browser.find_elements(By.ID, 'continue'))))
            browser.find_element(By.ID, 'continue').click()
            time.sleep(1)
            logPrint("ID:{0}, num:{1}".format("ap_password", len(browser.find_elements(By.ID, 'ap_password'))))
            browser.find_element(By.ID, 'ap_password').send_keys(AMAZON_LOGIN_PASSWORD)
            logPrint("ID:{0}, num:{1}".format("signInSubmit", len(browser.find_elements(By.ID, 'signInSubmit'))))
            browser.find_element(By.ID, 'signInSubmit').click()
            time.sleep(1)

            # 「携帯電話番号を追加する」が表示された場合、「後で」をクリック
            if len(browser.find_elements(By.ID, 'auth-account-fixup-phone-form')) > 0:
                browser.find_element(By.ID, 'ap-account-fixup-phone-skip-link').click()
                time.sleep(1)
            
            logPrint("Logged in now.")
    except Exception as e:
        logPrint('Failed to login.')
        logPrint(e)
        exit()

    # 購入する商品のページにアクセス
    logPrint("### Open Item Page ###")
    try:
        browser.get(ITEM_URL)
        time.sleep(1)
    except Exception as e:
        logPrint('Failed to open ITEM_URL.')
        logPrint(e)
        exit()

    # 商品のチェック
    #### 必要あれば、購入元のチェックや値段などの商品チェックを入れる #####

    # カートに追加
    logPrint("### Add To Cart ###")
    try:
        logPrint("ID:{0}, num:{1}".format("add-to-cart-button", len(browser.find_elements(By.ID, 'add-to-cart-button'))))
        browser.find_element(By.ID, 'add-to-cart-button').submit()
        time.sleep(1)
    except Exception as e:
        logPrint('Failed to add To Cart.')
        logPrint(e)
        exit()
    
    # レジに進む
    logPrint("### Proceed To Cashier ###")
    try:
        browser.get('https://www.amazon.co.jp/gp/cart/view.html/ref=nav_cart')
        logPrint("ID:{0}, num:{1}".format("sc-buy-box-ptc-button", len(browser.find_elements(By.ID, 'sc-buy-box-ptc-button'))))
        browser.find_element(By.ID, 'sc-buy-box-ptc-button').click()
        time.sleep(1)
    except Exception as e:
        logPrint('Failed to proceed to cashier.')
        logPrint(e)
        exit()

    # パスワードを聞かれたら、再度パスワードを設定
    try:
        # ログインが必要かチェック
        if len(browser.find_elements(By.ID, 'auth-alert-window')) > 0:
            logPrint("Logged in again")
            logPrint("ID:{0}, num:{1}".format("ap_password", len(browser.find_elements(By.ID, 'ap_password'))))
            browser.find_element(By.ID, 'ap_password').send_keys(AMAZON_LOGIN_PASSWORD)
            logPrint("ID:{0}, num:{1}".format("signInSubmit", len(browser.find_elements(By.ID, 'signInSubmit'))))
            browser.find_element(By.ID, 'signInSubmit').click()
            time.sleep(1)
    except Exception as e:
        logPrint('Failed to login.')
        logPrint(e)
        exit()

    '''
    # 購入内容の確認
    logPrint("### Confirm Payment ###")
    try:
        logPrint("ID:{0}, num:{1}".format("bottomSubmitOrderButtonId", len(browser.find_elements(By.ID, 'bottomSubmitOrderButtonId'))))
        browser.find_element(By.ID, 'bottomSubmitOrderButtonId').click()
        time.sleep(1)
    except Exception as e:
        logPrint('Failed to confirm payment.')
        logPrint(e)
        exit()
    '''

    # ブラウザを閉じる
    logPrint("### Close Browser ###")
    try:
        browser.close()
    except Exception as e:
        logPrint('Failed to close browser.')
        logPrint(e)
        exit()    

    logPrint("### FINISH autoPurcaser ###")

    return {
        'statusCode': 200,
        'body': json.dumps('autoPurcaser completed!')
    }
  • ITEM_URL[Amazon限定ブランド] キリン LAKURASHI アルカリイオンの水 PET (2L×9本)を選択しています。もし他の商品に変更する場合は、プログラムを修正する必要があるかもしれません。事前に動作するか確認してください。
  • 購入内容の確認の部分はあえて無効化しています。そのため、このプログラムを実行しても商品がカートに追加されるだけで、購入まで至りません。この部分は動作確認が完了したら有効化します。

 

「関数xxxxxが正常に更新されました。」と表示されれば、OKです。

 

 

 

Lambda関数にLambdaレイヤをアタッチする

以下に、Lambda関数にLambdaレイヤをアタッチする手順をまとめました。

  1. 前章同様に作成したLambda関数のページを開く。
  2. 「コード」タブを開き、下部にあるレイヤーで「レイヤーの追加」をクリックする。
  3. 「レイヤを追加」画面が開いたら、前ブログで作成したSelenium用のLambdaレイヤを設定して、右下の「追加」ボタンをクリックする。
設定項目設定例
レイヤーソース「カスタムレイヤー」を選択
カスタムレイヤSelenium用のLambdaレイヤで設定したレイヤ名(例:selenium)
バージョン1
(最大5つまで登録できる、レイヤに紐づけているZIPファイルがかわると
バージョンが自動で上がっていく)

 

  1. Lambda関数の設定画面に戻ったら、同様に下部にあるレイヤーで「レイヤーの追加」をクリックする。
  2. 「レイヤを追加」画面が開いたら、前ブログで作成したchrome関連ファイル用のLambdaレイヤを設定して、右下の「追加」ボタンをクリックする。
設定項目設定例
レイヤーソース「カスタムレイヤー」を選択
カスタムレイヤchrome関連ファイル用のLambdaレイヤで設定したレイヤ名(例:headless)
バージョン1
(最大5つまで登録できる、レイヤに紐づけているZIPファイルがかわると
バージョンが自動で上がっていく)

 

「関数xxxxxが正常に更新されました。」と表示されれば、OKです。

 

 

 

Lambda関数にIAMポリシーをアタッチする

以下に、Lambda関数にIAMポリシーをアタッチする手順をまとめました。

  1. 前章同様に作成したLambda関数のページを開く。
  2. 「設定」タブを開き、左メニューで「アクセス権限」を開く。
  3. 実行ロールに登録されているロール名のリンクをクリックする。

  

  1. IAMロールのページに飛ぶので、アクセス権限タブの「ポリシーをアタッチします」ボタンをクリックする。
  2. 「アクセス権限をアタッチする」画面が開くので、前ブログで作成したIAMロール「autoPurcaserPolicy」を選択して、右下の「ポリシーのアタッチ」ボタンをクリックする。

 

前ブログで作成したIAMロール「autoPurcaserPolicy」がアタッチされればOKです。

 

 

 

Lambda関数の基本設定を変更する

以下に、Lambda関数の基本設定を変更する手順をまとめました。

  1. 前章同様に作成したLambda関数のページを開く。
  2. 「設定」タブを開き、左メニューで「一般設定」を開く。
  3. 「編集」ボタンをリリックする。

 

  1. 「基本設定を編集」画面が開いたら、以下のように設定を変更して右下の「保存」をクリックする。
設定項目設定例
メモリ256MB
タイムアウト3分3秒
それ以外デフォルトの設定のまま

※デフォルトの設定だとタイムアウトやメモリ不足となるため、少し多めに変更しています。

 

「変更を保存しました。」と表示されれば、OKです。

 

 


以上でAWS Lambda関数の構築は完了です。
最後に自動注文スクリプトの動作確認をします。

 

 

自動注文スクリプトの動作確認

以下に、自動注文スクリプトの動作確認する手順をまとめました。

  1. 前章同様に作成したLambda関数のページを開く。
  2. 「テスト」タブを開き、「テスト」ボタンをクリックする。

 

実行結果が成功となればLambda関数の実行は正常終了しています!

  • 課金期間:121505ms
  • 使用中の最大メモリ:256MB

正常終了していれば、ログ出力は以下のようになっているはずです。

Function Logs
START RequestId: 7248ccbf-2b44-473f-b9fc-94a7d3ca1ca2 Version: $LATEST
2021/12/12 15:13:19 : ### START autoPurcaser ###
2021/12/12 15:13:19 : ### Get Secret Param from SSM ###
2021/12/12 15:13:20 : ### Open Top Page ###
2021/12/12 15:13:37 : ### Login ###
2021/12/12 15:13:41 : ID:nav-link-accountList, num:1
2021/12/12 15:13:52 : ID:ap_email, num:1
2021/12/12 15:13:56 : ID:continue, num:2
2021/12/12 15:14:00 : ID:ap_password, num:1
2021/12/12 15:14:02 : ID:signInSubmit, num:1
2021/12/12 15:14:15 : Logged in now.
2021/12/12 15:14:15 : ### Open Item Page ###
2021/12/12 15:14:33 : ### Add To Cart ###
2021/12/12 15:14:35 : ID:add-to-cart-button, num:1
2021/12/12 15:14:44 : ### Proceed To Cashier ###
2021/12/12 15:14:51 : ID:sc-buy-box-ptc-button, num:1
2021/12/12 15:15:18 : ### Close Browser ###
2021/12/12 15:15:21 : ### FINISH autoPurcaser ###
END RequestId: 7248ccbf-2b44-473f-b9fc-94a7d3ca1ca2

 

ご自分のAmazonアカウントにログインすると、カードの中に[Amazon限定ブランド] キリン LAKURASHI アルカリイオンの水 PET (2L×9本)」が入っているはずです!

 

もしうまくいかなかったら

以下についてご確認ください。

Lambda関数の実行結果が失敗 かつ 自動注文スクリプトが起動していない(ログに### START autoPurcaser ###がない)場合は、、、

  • ChromeのバージョンとChromeDriverのバージョンがあっているか。
  • Python,SeleniumのバージョンがPython 3.7、Selenium 3.141.0になっているか。
  • Lambdaレイヤーの設定に問題ないか。(前回ブログの事前準備の設定も含めて)
  • Lambda関数用のIAMポリシーの設定に問題ないか。(前回ブログの事前準備の設定も含めて)
  • Lambda関数のメモリ容量やタイムアウト値を変更したか。
    詳しくは「Lambda関数の基本設定を変更する」参照

 

Lambda関数の実行結果が失敗 かつ 自動注文スクリプトが起動している(ログに### START autoPurcaser ###がある)場合は、、、

  • ID:xxx, num:xxxのnumの数が1以上あるか。
    ※サイト内の入力箇所やボタンなどをIDで検索しているのですが、numが0の場合は、それが見つかっていないということです。商品ページにアクセスして、F12ボタンでHTMLを確認してそのIDが有効かどうか確認してください。
  • ログイン部分で失敗している場合、アマゾンIDとパスワードが間違っていないか。
  • ログイン部分で失敗している場合、パスワード以外に文字列を聞かれていないか。
    ⇒この場合は、一度手動でログインすることで改善するかもしれません。詳しくは「補足 自動注文スクリプトの課題」を参照ください。
  • 商品URLが有効なものか。

 

 

 

補足 自動注文スクリプトの課題

Amazonアカウントに何度もログインしているとパスワード以外にも、文字列を聞かれることがある。
この文字列はSeleniumで読み取ることができないため、これが出たら手動で一度ログインするしかないです。

 

 

 

さいごに

「ESP32でAmazon自動注文ボタン」に向けて、Lambda関数の作成から動作確認をしました。
引き続き、「ESP32でAmazon自動注文ボタン」の作成手順をまとめています。

前の手順

【ESP32でAmazon自動注文ボタン】Lambda関数で自動注文スクリプトの準備する
Amazon自動注文ボタンをESP32で作ろうとしています。 本記事はLambda関数で自動購入スクリプトの準備する手順をまとめました。

 

次の手順

【ESP32でAmazon自動注文ボタン】自作ボタンをクリックしたら、自動購入スクリプトが実行されるようにする
Amazon自動注文ボタンをESP32で作ろうとしています。 本記事は自作ボタンをクリックしたら、自動購入スクリプトが実行されるようにする手順をまとめました。

 

手順全体や関連情報まとめ

 

 

 

参考情報

 

以上!

 

コメント

タイトルとURLをコピーしました