あけましておめでとうございます。
新年一発目のPython記事になります(まとめ記事などはおいておいて)。
今回のテーマはseleniumなどを利用してAmazonから領収書を自動取得するコードを書いていこうと思います。昔このコード書いたよなぁと思いつつ、動かなくなっていたのでその理由(2要素認証対応)と実際の領収書を取得するまでシリーズ何回かに分けて解説していきたいと思います。
まずは、第1回目、何はともあれ2FAをパスしてAmazonにログインするところまでやっていきます。
利用環境の説明
今回利用する環境は以下になります。
OS ・macOS Catalina 10.15.17 コード環境 ・pycharm ・python 3.8 ・venv(仮想環境) ・homebrew(oath-toolkitをsubprocessで利用しますので、今回はbrewが必要です) ・requirments.txtファイル(モジュール群) beautifulsoup4==4.9.3 certifi==2020.12.5 chardet==4.0.0 idna==2.10 requests==2.25.1 selenium==3.141.0 soupsieve==2.1 urllib3==1.26.2
目新しいものはないのですが、実は2FAをpassするために、oath-toolkitを利用しますので途中からbrewを利用したterminal環境も利用しますので、ご注意ください。
事前準備:terminalでoath-toolkitをインストール
さて、コードを走らせる前に、terminalでhomebrewにoath-toolkitをインストールしておいてください。これを忘れると以下のコードは動きません。
brew install oath-toolkit
oath-toolkitを利用することで、2要素認証で利用されるTOTP(後ほど紹介)ベースのOTPが作成可能です。
今回利用するコード
今回は2FAをPassしてAmazonにログインするところまでですので、以下のコードになります。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.expected_conditions import visibility_of
import bs4,requests,time,os,sys,random,re,csv
url = "https://www.amazon.co.jp/ap/signin?_encoding=UTF8&openid.assoc_handle=jpflex" \
"&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select" \
"&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select" \
"&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0" \
"&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0" \
"&openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.amazon.co.jp%2Fgp%2Fyourstore%2Fhome%3Fie%3DUTF8%26action" \
"%3Dsign-out%26path%3D%252Fgp%252Fyourstore%252Fhome%26ref_%3Dnav_AccountFlyout_signout%26signIn%3D1%26useRedirectOnSuccess%3D1"
e_mail = "e-mailアドレス"
password = "パスワード"
wd = webdriver.Chrome("pathを入力")
wd.get(url)
"""
メールアドレスの入力+送信
"""
wd.find_element_by_xpath('//*[@id="ap_email"]').send_keys(e_mail)
wd.find_element_by_xpath('//*[@id="continue"]').click()
"""
passwordの入力
"""
wd.find_element_by_xpath('//*[@id="ap_password"]').send_keys(password)
wd.find_element_by_xpath('//*[@id="signInSubmit"]').click()
"""
2FAコードの取得+入力
oathtoolを利用
"""
import subprocess, re
twoFA = ['oathtool', '--totp', '--base32', 'アカウント追加用のキー']
print(subprocess.check_output(twoFA))
LoginPass_array = re.findall(r'\d+', subprocess.check_output(twoFA).decode('utf-8'))
LoginPass = LoginPass_array[0]
"""
認証アプリ画面への移動
"""
wd.find_element_by_xpath('//*[@id="auth-select-device-form"]/div[1]/fieldset/div[3]/label/input').click()
wd.find_element_by_xpath('//*[@id="auth-send-code"]').click()
"""
OTP入力画面
"""
wd.find_element_by_xpath('//*[@id="auth-mfa-otpcode"]').send_keys(LoginPass)
wd.find_element_by_xpath('//*[@id="auth-signin-button"]').click()
大昔のコードだけあって、何も考えずにx-pathで全て取得している感じが見受けられるコードですね汗 非常にかっこ悪いコードではありますが、リファクタすることが今回の目的ではなかったので、そのまま利用しています。綺麗に書きたい方は、自分で書き直してもらえるといいかと。
メールアドレス・パスワードの入力やボタンクリックなどは、find_element_by_xpathで取得して、send_keys(“入力したいデータ”)で設定して、clickしているだけなので、説明は割愛します。
多分、一度でもseleniumを利用された方にとっては特段難しいことはない部分だと思います。
コードの説明〜2要素認証をパスする処理〜
今回追加したコードで、解説すべき唯一の部分は、以下の2要素認証をパスするためのコードだと思います。
対象コード
"""
2FAコードの取得+入力
oathtoolを利用
"""
import subprocess, re
twoFA = ['oathtool', '--totp', '--base32', 'アカウント追加用のキー']
print(subprocess.check_output(twoFA))
LoginPass_array = re.findall(r'\d+', subprocess.check_output(twoFA).decode('utf-8'))
LoginPass = LoginPass_array[0]
これだけパッとみて何をやっているかがわかる方がいれば、多分あなたはノンプロではありません笑
理解までのステップ
普通のノンプロでは少しよくわからない部分が多いと思うので、step by stepで理解して行ってください。理解というか、なぜこのコードになるかの前提含めた把握方法は以下です。
- step12FAで利用されるOTPの基本を理解する
・OTPを作成するためのTOTP( Time-based One-Time Password )の概要理解
・TOTPを利用した身近なツール
・OTPをTOTPベースで作成するための方法
(参考)OATH規格(initiative for Open AuTHentication)について。TOTPの共通規格
- step2oath-toolkitを理解する
・oath-toolkitでできること
・base32である理由
- step3terminalでoath-toolkitを使ってみる
・実際にterminalで試してみよう
- step4subprocessモジュールの理解
・subprocessモジュールでできること
流石にこれを全て解説していくとかなり長くなっていくかもしれませんので、詳細は別記事にさせていただくとして、amazonでのアカウント追加キーの取得方法とsubprocessで実行している事項に絞って少し解説していきます。
amazonでのアカウント追加キーの取得方法
amazonの2FAをパスするためには、2FA用のアカウントキーを入手する必要があります。これは、以下の手順で取得可能なので、手順を踏んで入手してください。
【アカウントサービス→ログインとセキュリティ】
まずは、アカウントサービスをクリックして、【ログインとセキュリティ】を選択してください。

【ログインとセキュリティ画面で2段階認証の設定の編集を選択】
ログインとセキュリティ画面へ遷移した後、2段階認証の設定の編集ボタンをクリックします。

【認証アプリ→新しいアプリの追加を選択】
2段階認証の設定画面に遷移した後、認証アプリカテゴリないにある【新しいアプリの追加】をクリックします。

【バーコードをスキャンできませんか?をクリックして、出てきたキーを取得する】
新しいアプリの追加ボタンをクリックすると、手段の追加画面に遷移します。遷移後、バーコードをスキャンできませんか?をクリックすると、追加キーが表示されますので、これで入手可能です。

これで、subprocessでoath-toolkitを使ってOTPを利用するために必要な追加キーが入手できました。
subprocessで実行している内容
それでは、subprocessで実行している内容の解説もしていきます。
まず、そもそもsubprocessライブラリが何をしているのかをざっとおさらいしていきます。
subprocessライブラリの基本
名前の通り、Pythonを実行しているプロセスからサブプロセスを生成するためのものです。同期処理・非同期処理という言葉自体は、Javascriptでもよく利用するので、GASを使われる人たちも馴染みのあるものかと思います。そのような同期・非同期問わずで、pythonを複数プロセス走らせるためのライブラリがsubprocessライブラリです。
run処理で同期処理が、popen処理で非同期処理が実行可能です。今回はrun処理の一つである、subprocess.check_output()メソッドを利用しています。
subprocess check_outメソッドについて
これは、引数を与えて、その引数で実行して、その出力結果を返すメソッドです。
subprocess.check_output
(args, *, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=None, timeout=None, text=None, **other_popen_kwargs)¶
今回のケースでは、check_outのargsに以下のような引数を渡しています。
twoFA = ['oathtool', '--totp', '--base32', 'アカウント追加用のキー']
これは、別回で説明しようかと思っている、terminalでoathtoolを使ってtotp(Time-based One-Time Password)を作成するためのもので、terminalで入力するスクリプトをカンマ区切りで入力して配列として渡しています。
実行結果の確認
以下のスクリプト部分を実行した結果をまずは確認してください。
twoFA = ['oathtool', '--totp', '--base32', 'アカウント追加用のキー']
print(subprocess.check_output(twoFA))
このような結果がターミナルで表示されると思います。表示されるエンコードされた値は人によって異なりますので、出力結果のフォーマットだけ確認できればOKです。
b'644381\n'
返ってきている結果は、バイト列の値なので、これを通常の6桁数値に置き換えるため、以下のように処理をしています。
LoginPass_array = re.findall(r'\d+', subprocess.check_output(twoFA).decode('utf-8'))
print(LoginPass)
また、これの実行結果が以下のようになります。配列で返ってきていますね。
['644381']
配列のまま、seleniumのsend_keyメソッドではinputに送り込めないので、配列から値を取り出す処理をします。
LoginPass = LoginPass_array[0]
ここまでセットで実施しているのが、今回の解説対象となった以下のコード部分です。
"""
2FAコードの取得+入力
oathtoolを利用
"""
import subprocess, re
twoFA = ['oathtool', '--totp', '--base32', 'アカウント追加用のキー']
print(subprocess.check_output(twoFA))
LoginPass_array = re.findall(r'\d+', subprocess.check_output(twoFA).decode('utf-8'))
LoginPass = LoginPass_array[0]
順を追っていくとなんとなくわかってきましたかね?
まとめ
さて、”selenium・pythonを使ってAmazonの領収書を自動取得する方法〜シリーズ1:2要素認証をパスする方法〜”の紹介でした。
その中でも、今回はAmazonの2要素認証を通過して、ログインする部分までの処理をコード及びコード内容含めて解説してきました。
次回は、実際に領収書を取得して、ローカルに保存していく部分のコードを紹介していきます。
関連記事: seleniumでAmazon領収書をPDF保存する方法関連記事
確定申告の時期にいつも必要になる領収書。そしてよく使うAmazon。そんなAmazonの領収書を一つ一つクリックして保存するなんて、時間的にも精神的にも辛い。というわけで、Amazonの領収書をseleniumを使って自動で指定フォルダにPDF保存する方法の紹介をするシリーズです。