【Python】Python + SeleniumでCSVからBitwardenへID/PWを自動登録するプログラムを作成してみる


<h2>はじめに</h2>
パスワード管理って大変ですよね。

今までTrendMicroのPassword Managerを使用してきたんですが、
Chromeの拡張機能がたまに使い物にならない時があって、なんかいいのないかなと思っていたところ
Bitwardenが無料でいい感じだったので乗り換えを決めました。

Password ManagerでパスワードファイルをCSV出力して、
Bitwardenでインポートしたら乗り換えは一瞬かと思ったんですが、
インポートのオプションでPassword Managerが選択肢になかったためSeleniumで自動化させてみました。


<h2>環境</h2>
MacOS: Catalina 10.15.7
Python: 3.8.3
Selenium:: 3.141.0
Google Chrome: 87.0.4280.88
chromedriver: 87.0.4280.88

<h2>Bitwardenへの会員登録</h2>
まずはBitwardenに会員登録しましょう。
UIが今っぽくて良いですね。

https://vault.bitwarden.com/#/

<h2>Seleniumの環境構築</h2>
こちらの方の記事を参考にして設定しました。

>Python + Selenium で Chrome の自動操作を一通り
>https://qiita.com/memakura/items/20a02161fa7e18d8a693

以下のコマンドでSeleniumをインストールします。

```
pip install selenium
```

chromedriverをpip installで入れてみたのですが、
私の場合Chromeとのバージョンが不一致でエラーが出たため、
以下のサイトからマッチするバージョンのchromedriverを落としてきました。

>http://chromedriver.chromium.org/downloads

ダウンロード後はお好きな場所において下さい。
私はライブラリ配下に以下のようにフォルダを作成して格納しました。

```
/Library/ChromeDriver/chromedriver
```

これでSeleniumのセットアップは完了です。

<h2>必要なライブラリのインポート</h2>
今回使用するライブラリたちをインポートしていきます。

```
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
import datetime
import csv
import codecs
```

<h2>chromedriverでBitwardenにアクセス</h2>
私のようにchromedriverをサイトからダウンロードした人は
下記のようにpathを指定してあげる必要があります。

```
driver = webdriver.Chrome(executable_path='/Library/ChromeDriver/chromedriver')

# アクセスするウェブサイトのURLを指定
driver.get('https://vault.bitwarden.com/#/')

# ログイン画面に遷移したら2秒待機
time.sleep(2)

# ブラウザを閉じる
driver.quit()
```
chrome driverをpip installした人は

```
import chromedriver_binary
```
を追加してあげて下さい。

これでBitwardenにアクセスするプログラムができました。

<h2>ログインの実装</h2>
ログイン画面まで行けたので、ログインIDとパスワードを入力して、ログインボタンをクリックするところまでやってみます。

```
driver = webdriver.Chrome(executable_path='/Library/ChromeDriver/chromedriver')

# アクセスするウェブサイトのURLを指定
driver.get('https://vault.bitwarden.com/#/')

# ログイン画面に遷移したら2秒待機
time.sleep(2)

# ログインIDを入力
login_id = driver.find_element_by_name("Email")
login_id.send_keys("自分のメールアドレス")

# パスワードを入力
master_password = driver.find_element_by_name("MasterPassword")
master_password.send_keys("自分のパスワード")

# ログインボタンクリック
login_btn = driver.find_element_by_xpath("/html/body/app-root/app-frontend-layout/app-login/form/div/div/div/div/div[4]/button")
login_btn.click()

time.sleep(5)

# ブラウザを閉じる
driver.quit()
```
ログインID/パスワードのフォームは

```
find_element_by_name
```
で探すことができます。
ログインボタンはnameの指定がなかったため、XPATHで指定してます。

ブラウザのF12ボタンを押して、開発者ツールで該当箇所のnameだったり、idだったりを探して
それに対応する"find_element_by~"で指定する感じです。

これでログインまではできました。

<h2>CSVファイルから登録したいサイトのURL/ID/PWを読み込んで登録していく</h2>
登録したいサイトが２００件近くあったので、手作業は無理と判断。
CSVファイルから読み込んで登録していきます。

ちなみに、今回のCSVファイルは以下のようなフォーマットになっています。

```Passwords.csv
サイト名,サイトURL,ユーザー名,パスワード
```

```
driver = webdriver.Chrome(executable_path='/Library/ChromeDriver/chromedriver')

# アクセスするウェブサイトのURLを指定
driver.get('https://vault.bitwarden.com/#/')

# ログイン画面に遷移したら2秒待機
time.sleep(2)

# ログインIDを入力
login_id = driver.find_element_by_name("Email")
login_id.send_keys("自分のメールアドレス")

# パスワードを入力
master_password = driver.find_element_by_name("MasterPassword")
master_password.send_keys("自分のパスワード")

# ログインボタンクリック
login_btn = driver.find_element_by_xpath("/html/body/app-root/app-frontend-layout/app-login/form/div/div/div/div/div[4]/button")
login_btn.click()

time.sleep(5)

# CSVファイルの読み込み
# サイト名で日本語を扱うので、文字コードはShit-JISを選択
with codecs.open('CSVファイルのPATH',"rb", "Shift-JIS", "ignore") as f:
    for row in reader:
        # CSVファイルの１行目にヘッダーがある場合は、以下で読み込みをスキップさせる
        next(reader)

        #ログインID/PWと同じ要領で各フォームの要素を取得し、クリック/入力をしていく

        # アイテム追加
        add_item = driver.find_element_by_xpath("/html/body/app-root/app-user-layout/app-vault/div/div/div[2]/div/div/button")
        add_item.click()

        # 名前の入力
        enter_name = driver.find_element_by_name("Name")
        enter_name.send_keys(row[0])
        print("名前を入力しました")

        # ユーザー名の入力
        enter_username = driver.find_elements_by_id("loginUsername")
        enter_username[0].send_keys(row[2])
        print("ユーザー名を入力しました")

        # パスワードの入力
        enter_password = driver.find_elements_by_id("loginPassword")
        enter_password[0].send_keys(row[3])
        print("パスワードを入力しました")

        # URLの入力
        enter_url = driver.find_elements_by_xpath("/html/body/app-root/app-user-layout/app-vault/app-modal/app-vault-add-edit/div/div/form/div[2]/div[5]/div[1]/div/input")
        enter_url[0].send_keys(row[1])
        print("URLを入力しました")

        # 保存ボタンクリック
        click_save = driver.find_elements_by_xpath("/html/body/app-root/app-user-layout/app-vault/app-modal/app-vault-add-edit/div/div/form/div[3]/button[1]/span")
        click_save[0].click()

        # 次の入力まで２秒待機
        time.sleep(2)

# ブラウザを閉じる
driver.quit()
```

nameやidで拾ってこれないモノは、XPATHで指定しています。

```
enter_username[0].send_keys(row[2])
```
などのように、[0]がついているモノは変数の型がlistのため必要とのこと。
これを付けずに実行したら

```
AttributeError: 'list' object has no attribute 'send_keys',
```
というエラーが出ました。

こちらを参考にして、取りえあず真似してみたらできた。
理由はよくわからないけど、動いたからひとまず放置。。。

>https://programmersought.com/article/33681438536/

以上です。


<h3>参考</h3>
>Python + Selenium で Chrome の自動操作を一通り
>https://qiita.com/memakura/items/20a02161fa7e18d8a693

>AttributeError: 'list' object has no attribute 'send_keys', python+selenium Enables automatic login of QQ space pages.
>https://programmersought.com/article/33681438536/