見出し画像

PythonでAm●zon自動購入bot作ってみた⑥(Am●zonのweb画面の自動操作)

押忍!

某youtuberの登場キャラでおなじみ「鎖骨打ちのサトJ」です!

本編に戻りましていよいよ終盤ってか最終回かもです。

前回の記事はこちら

有料ですがためになること書いたつもりなんでぜひ購入お願いします!

こちらのページを見たうえで読むとより分かりやすいかと思います。

今回の記事はこの箇所の説明

簡易的な処理の流れ

この箇所は実際の処理操作になる箇所です。

Am●zonは画面遷移、表示パターンが結構多いので苦戦はしたのですが

なんとか最低限動きそうなものを作れたので参考にしていただけたら幸いです。

■Am●zonログイン

とりあえずコード載せます

def Login(driver:webdriver.Chrome,login:str,password:str,loginUrl:str):
   driver.get(loginUrl)
   try:
       driver.find_element_by_name("email").send_keys(login)
       driver.find_element_by_id("continue").click()
       driver.find_element_by_name("password").send_keys(password)
       driver.find_element_by_name("rememberMe").click()
       driver.find_element_by_id("signInSubmit").click()
       print(datetime.now())
       print("ログイン出来ました。念のため確認をお願いします。")
       print("ログイン出来ていない場合は手動でログインしてください。")
   except:
       print("ログインできませんでした。")
       print("購入処理実行時刻前までに手動でやり直してください。")
   finally:
       print("手動でログインする場合は「ログインしたままにする」にチェックをいれてください。")

こちらは特に複雑な操作はなく、タグ名を取得して動かしている感じです。

これを見ている方は大体ご存じかと思いますが

driver.find_element_by_●●([htmlタグのIDやName])

で押したいボタンや入力したいテキストのエレメントを取得

driver.find_element_by_●●([htmlタグのIDやName]).click()

でボタンを押下

driver.find_element_by_●●([htmlタグのIDやName]).sendkey([入力したい文字])

でテキストボックスに文字を入力

です。

なお

driver.find_element_by_●●([htmlタグのIDやName]).click()

でボタンを押せなかった場合は

driver.find_element_by_●●([htmlタグのIDやName]).submit()

も試してみるといいかもです。

で今回はヘッダーレスを想定していないのでIDやパスワード間違えたなどにより先に進めなかった場合を考慮してtry-exceptで囲んで落ちた時は手動でログインしてもらう方法をとってます。

なぜヘッダーレスにしたかはくどいようですが有料記事に書いてます

​■Am●zonでの購入処理

こっちもまずコード全部載せます


def Purchase(driver:webdriver.Chrome,purchaseGoodsUrl:str,checkColor:str,checkSize:str,quantity:str):
       
   TimeUtiltys.MakeSleep(0.88)

   driver.get(purchaseGoodsUrl)
   
   try:
       # カラー指定がある場合
       if checkColor != "":
           for _ in range(5):
               try:
                   li:WebElement = driver.find_element_by_id("color_name_" + checkColor)
                   li.find_element_by_tag_name("button").click()
                   break
               except Exception as e:
                   select = Select(driver.find_element_by_id("native_dropdown_selected_color_name"))
                   select.select_by_index(int(checkSize))  # optionタグを選択状態に
                   break
               except Exception as e:
                   pass
           else:
               print("[error]カラーが存在しないか選択できませんでした。")
               driver.quit()
               return

       # サイズ指定がある場合
       if checkSize != "":
           for _ in range(5):
               try:
                   select = Select(driver.find_element_by_id("native_dropdown_selected_size_name"))
                   select.select_by_index(int(checkSize))  # optionタグを選択状態に
                   break
               except Exception as e:
                   li:WebElement = driver.find_element_by_id("size_name_" + checkSize)
                   li.find_element_by_tag_name("button").click()
                   break
               except Exception as e:
                   pass
           else:
               print("[error]サイズが存在しないか選択できませんでした。")
               driver.quit()
               return

       # 購入数指定がある場合
       if quantity != "":
           for _ in range(5):
               try:
                   select = Select(driver.find_element_by_id("quantity"))
                   select.select_by_value(quantity)  # optionタグを選択状態に
               except Exception as e:
                   pass  
               else:
                   break  # 失敗しなかった時はループを抜ける
           else:
               print("[error]個数選択ができませんでした。")
               driver.quit()
               return
       
       # 通常の注文か定期おとく便の選択が出てきたら通常の注文を選択する
       try:
           li:WebElement = driver.find_element_by_id("newAccordionRow")
           li.find_element_by_class_name("a-accordion-row-a11y").click()
       except Exception as e:
           # なかったらスルー
           pass

       for _ in range(5):  # 最大5回実行。カラー、サイズ指定があるやつはすぐ表示されないことがあるため
           try:
               driver.find_element_by_id("buy-now-button").click()  # 失敗しそうな処理
           except Exception as e:
               pass  
           else:
               break  # 失敗しなかった時はループを抜ける
       else:
           print("[error]「今すぐ買う」ボタンが存在しないか押せません。")
           driver.quit()
           return
       
       for _ in range(10):  # 最大10回実行
           try:
               # 画面遷移した場合
               driver.find_element_by_name("placeYourOrder1").click()  
               print(datetime.now())
               print("[success]購入成功")
               sleep(5)
               driver.quit()
               break
           except Exception as e:
               # iframeになった場合                     
               driver.switch_to_frame("turbo-checkout-iframe")
               driver.find_element_by_xpath('//*[@id="turbo-checkout-pyo-button"]').click()
               print(datetime.now())
               print("[success]購入成功")
               sleep(5)
               driver.quit()
               break
           except Exception as e:
               pass           
       else:
           print("[error]購入失敗")
           driver.quit()
           return
   
   except Exception as e:
       print("[error]存在しないページか対応していないページです。あるいは何らかの不具合が発生してます")
       driver.quit()

まず「TimeUtiltys.MakeSleep(0.88)」ですがこれは訳すと

「0.88秒待機する。」

です。

例えば12/1 00:00:00から販売開始の商品だと11/30 23:59:59だとまだ購入ボタンは押せません。1秒前で始めたとしてもおそらくプログラムの処理のほうが早いため12/1になる前に開いてしまいます。

そのため、さらに1秒以内の時間で待機させることによってなるべくちょうどに開けるように調整しております。

なんでここは1秒以内ならお好みで大丈夫かと思います。

なんも指定せず「今すぐ購入」が押せる商品ならいいですが、服とかだいたいサイズとかカラーとか選択しないと押せないんで最低限以下の項目を設定できるようにしました

・カラー

・サイズ

・購入個数

if checkColor != "":
   for _ in range(5):
       try:
           li:WebElement = driver.find_element_by_id("color_name_" + checkColor)
           li.find_element_by_tag_name("button").click()
           break
       except Exception as e:
           select = Select(driver.find_element_by_id("native_dropdown_selected_color_name"))
           select.select_by_index(int(checkSize))  # optionタグを選択状態に
           break
       except Exception as e:
           pass
    else:
       print("[error]カラーが存在しないか選択できませんでした。")
       driver.quit()
       return
# サイズ指定がある場合
if checkSize != "":
   for _ in range(5):
       try:
           select = Select(driver.find_element_by_id("native_dropdown_selected_size_name"))
           select.select_by_index(int(checkSize))  # optionタグを選択状態に
           break
       except Exception as e:
           li:WebElement = driver.find_element_by_id("size_name_" + checkSize)
           li.find_element_by_tag_name("button").click()
           break
       except Exception as e:
           pass
   else:
       print("[error]サイズが存在しないか選択できませんでした。")
       driver.quit()
       return

# 購入数指定がある場合
if quantity != "":
   for _ in range(5):
       try:
           select = Select(driver.find_element_by_id("quantity"))
           select.select_by_value(quantity)  # optionタグを選択状態に
       except Exception as e:
           pass  
       else:
           break  # 失敗しなかった時はループを抜ける
   else:
       print("[error]個数選択ができませんでした。")
       driver.quit()
       return

各処理それぞれ処理全体をfor文try-exceptで囲むことで読み込みよりプログラムが早く動いてエレメントをとれなかったということを防いでます。

カラーは最初の入力フォームで一番左(または上)を0としてN番目かの数値を入力してもらいます。

例えば左から2番目のカラーを選択したい場合は「1」を入力してもらいます。

この「1」が以下の<li>タグのエレメントを取得します。

無題

この<li>タグの階層の赤枠がボタンなのですがボタンの「Class」はほかのタグにも使われており、IDの振り方もイレギュラーなんでそのままだとボタンのエレメントをとれません。

なんでいったんどの<li>タグを見るか指定して、その配下にあるボタンという形でエレメントを取得しております。(この<li>タグ配下にはボタンは一つしかありません。

スクリーンショット 2021-10-02 01.11.47

また上の商品みたいにカラーがドロップダウンの場合もあるので<li>タグが見つからなくて落ちた場合は<Select>タグを探しに行くようにしてます。

selenium.webdriver.support.select

上記を使ってindexでカラーを指定してます。

サイズや個数も同様な感じでやってます。

スクリーンショット 2021-10-02 01.27.24


中にはこのようにカラー選択したりすると「定期おトク便」になるものもあり、これだと「今すぐ購入」が押せないんで通常の注文を選択します。

これはパターンなのでtry-exceptで囲んで落ちた場合はスルーさせてます

for _ in range(10):  # 最大10回実行
   try:
       # 画面遷移した場合
       driver.find_element_by_name("placeYourOrder1").click()  
       print(datetime.now())
       print("[success]購入成功")
       sleep(5)
       driver.quit()
       break
   except Exception as e:
       # iframeになった場合                     
       driver.switch_to_frame("turbo-checkout-iframe")
       driver.find_element_by_xpath('//*[@id="turbo-checkout-pyo-button"]').click()
       print(datetime.now())
       print("[success]購入成功")
       sleep(5)
       driver.quit()
       break
   except Exception as e:
       pass           
else:
   print("[error]購入失敗")
   driver.quit()
   return

これが最後の購入処理になりますが「今すぐ購入」を押した後の画面は2パターンあります

スクリーンショット 2021-10-02 01.21.40

上のような画面にいって「注文を確定する」ボタンを押すパターン。

こちらは特に問題ないです。

プログラムではすぐ押せるようにクレジットカード払いを推奨させてます。

スクリーンショット 2021-10-02 01.28.48

もう一つがこの画面です。こちらがやっかいでそのまま「注文を確定する」ボタンのエレメントを取得できません。

この背景が薄暗くなるのは「iframe」というもので「iframe」からエレメントを取得するように変えないといけません。

driver.switch_to_frame("turbo-checkout-iframe")

この「switch_to_frame」で「iframe」のIDを指定してあげると「iframe」内のエレメントを取得できるようになります。

■終わりに

いかがだったでしょうか?

自分も結構Am●zonの自動購入ツールのコードを調べましたが実際にちゃんと買えるようなコードってのはなかなか見つかりませんでした。

これパクれば最低限動かせるんでいい記事になったのではないでしょうか?

(あとで有料にしようかなw)

次回は最終回。

「pyinstaller」でexe化(app化)したものを公開しようかと思います。

exe化はできたのですが当方MacPCを持っていないため探しているのでしばらく先になるかと思いますがしばしお待ちください



この記事が気に入ったらサポートをしてみませんか?