Python＋SeleniumでSAISONカードの明細をCSVダウンロードする

PythonとSeleniumを使って、SAISONカードサイト「Netアンサー」にログインして明細データを取得（CSVダウンロード）する方法を試してみました。

サンプルコード全文も記事の後半に載せているので参考になれば幸いです。そしてもっと上手いやり方ご存知の方いらっしゃったらぜひ教えてください＾＾

※2021/04/21時点の情報です。

## 環境
- Windows 10
- Python 3.8
- Selenium 3.141.0


## はじめに
ログインまでの部分や、webdriverのプロファイル設定に関する部分については、以下記事をご確認ください。

https://qiita.com/yoshi2045/items/7f64ff193e0d29a40c2f

↑ログインするところまでのサンプルです。Netアンサーのログインは他サイトとは違って特殊な動きをするので、ちょっとコツがいります。（本記事のサンプルコードにもログイン部分あります）

https://qiita.com/yoshi2045/items/be739680bceb2f2df6a2

↑サンプルのwebdriverは、Firefoxを使っています。CSVダウンロードするにあたって、Firefoxのプロファイル設定が必要なので、そのあたりを解説しています。

## 対象画面まで遷移させる
まず、SAISONカードのログイン後TOP画面から明細ダウンロード画面までどういう経路で遷移できるかを確認してみます。

＜画面フロー＞

1. TOP画面
2. ご明細確認画面 ← ここでCSVダウンロード

上記フローで目的の画面まで遷移できることが分かりました。では続いて、具体的に各画面ごとの遷移処理を見ていきましょう。

◆ログイン後のTOP画面

|![20210420-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459893/a9bf8bae-cfc8-c331-ced9-e0f00714c2b6.png)|
|:--|

TOP画面からご明細確認画面への遷移は、画面左部にある「ご利用明細確認」ボタンをクリックします。

```python
# ご明細確認画面遷移
browser.find_element_by_id("ln02_01_meisai").click()

# ページロード完了まで待機
WebDriverWait(browser, 10).until(
    ec.presence_of_element_located((By.ID, "calendarMeisai"))
)
print("ご利用明細確認画面遷移成功")
```

### オーバーレイ広告対策
時々、ログイン直後にオーバーレイ広告が表示されるときがあります。

```python

# オーバーレイ広告表示対策
# 時々オーバーレイ広告が表示される。広告枠以外をクリックすると消えるので念の為bodyタグ指定でクリック
browser.find_element_by_xpath("//body").click()
```

◆ご明細確認画面

ご明細確認画面に遷移すると、当月の明細情報が表示されます。

|![20210420-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459893/d5aa3768-940a-a978-24f1-26aebc79db0d.png)|
|:--|

別の月を指定する場合は「ほかの月の明細を確認」ボタンをクリックします。すると、対象月を選択するためのカレンダーが表示されるので、続けて対象月を選択します。

|![20210420-003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459893/3d3d9a34-8a50-6d0d-58e5-0954b10d4673.png)|
|:--|

下記は、1月を選択した場合の例です。

```python
# 対象月選択用カレンダーを表示し、月を選択
browser.find_element_by_id("calendarMeisai").click()
month = "1"
browser.find_element_by_css_selector(".month{0}".format(month)).click()
```

## CSVダウンロード実行

月を選択すると、ご利用明細画面が再表示されます。

|![20210420-004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459893/fe803e67-9fa9-5856-4db0-e42e33876444.png)|
|:--|

あとは「CSVダウンロード」ボタンをクリックすれば、ファイルをダウンロードすることができます。

```python
# CSVダウンロード実行
browser.find_element_by_xpath("//img[@alt='CSVダウンロード']").click()
```

これで無事、CSVダウンロードできると思います。

以上、Python＋SeleniumでSAISONカードの明細をCSVダウンロードする方法でした！


## サンプルコード全文

```python
#
# セゾンカードサイトへログインしてCSVダウンロード
#

import json
from selenium import webdriver
from selenium.webdriver import Firefox, FirefoxOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

# 設定ファイルを取得
login_info = json.load(open("login_info.json", "r", encoding="utf-8"))

# ログインサイト名
site_name = "card_saison"

# ログイン画面URL
url_login = login_info[site_name]["url"]

# ユーザー名とパスワードの指定
USER = login_info[site_name]["id"]
PASS = login_info[site_name]["pass"]

# ダウンロードフォルダ
# 相対パス指定はできない模様
# dl_folder = r"..\dl-data"
# TODO: ダウンロードフォルダのパスは適宜変更してください
dl_folder = r"ダウンロードフォルダのパスを指定"

# オプション設定
options = FirefoxOptions()

# ヘッドレスモードを有効にする
options.add_argument('--headless')

# プロファイル設定
fp = webdriver.FirefoxProfile()

# ダウンロードフォルダ指定
# 0: デスクトップ、1:システム規定のフォルダ、2:任意の指定フォルダ
fp.set_preference("browser.download.folderList", 2)
fp.set_preference("browser.download.dir", dl_folder)

# ダウンロードマネージャウィンドウを表示させない
fp.set_preference("browser.download.manager.showWhenStarting", False)

# MIMEタイプを設定
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream;text/csv")

# Firefoxを起動する
browser = Firefox(options=options, firefox_profile=fp)

# ログイン画面取得
# add_cookie()呼び出す前に一度get()で対象画面取得する必要あり
browser.get(url_login)

# クッキー情報を追加
browser.add_cookie(
    {"name": login_info[site_name]["onetime_name"],
     "value": login_info[site_name]["onetime_value"],
     "path": login_info[site_name]["onetime_path"],
     "domain": login_info[site_name]["onetime_domain"]
     }
)
browser.add_cookie(
    {"name": login_info[site_name]["auto_comp_name"],
     "value": login_info[site_name]["auto_comp_value"],
     "path": login_info[site_name]["auto_comp_path"],
     "domain": login_info[site_name]["auto_comp_domain"]
     }
)
browser.add_cookie(
    {"name": login_info[site_name]["login_id_name"],
     "value": login_info[site_name]["login_id_value"],
     "path": login_info[site_name]["login_id_path"],
     "domain": login_info[site_name]["login_id_domain"]
     }
)

# Cookie情報取得
# 追加したクッキーの情報を参照する場合は以下コード追加
# cookieList = browser.get_cookies()

# クッキー追加後に再度ログイン画面取得
browser.get(url_login)

# Cookie設定が反映されているか確認（画面キャプチャ取る）
# browser.save_screenshot("../screenshots/card_saison/login.png")

# # 入力
e = browser.find_element_by_id("password")
e.clear()
e.send_keys(PASS)

# ログイン
button = browser.find_element_by_xpath("//input[@value='ログイン']")
button.click()

# ページロード完了まで待機
WebDriverWait(browser, 10).until(
    ec.presence_of_element_located((By.ID, "user_name"))
)

# WebDriverWait()でうまく待機できない場合がある
# その時は代替案としてスリープで強制待機させる
# time.sleep(3)

# ログインできたか確認（画面キャプチャ取る）
# browser.save_screenshot("../screenshots/card_saison/home.png")

# オーバーレイ広告表示対策
# 時々オーバーレイ広告が表示される。広告枠以外をクリックすると消えるので念の為bodyタグを指定でクリックしておく。
browser.find_element_by_xpath("//body").click()

# ご明細確認画面遷移
browser.find_element_by_id("ln02_01_meisai").click()

# ページロード完了まで待機
WebDriverWait(browser, 10).until(
    ec.presence_of_element_located((By.ID, "calendarMeisai"))
)
print("ご利用明細確認画面遷移成功")

# 対象月選択用カレンダーを表示し、月を選択
browser.find_element_by_id("calendarMeisai").click()
month = "1"
browser.find_element_by_css_selector(".month{0}".format(month)).click()

# 前年のカレンダー表示させる場合
# browser.find_element_by_css_selector(".calendar-menu-prev").click()
# 翌年のカレンダー表示させる場合
# browser.find_element_by_css_selector(".calendar-menu-next").click()

# ページロード完了まで待機
WebDriverWait(browser, 10).until(
    ec.presence_of_element_located((By.XPATH, "//img[@alt='CSVダウンロード']"))
)
print("ご利用明細表示成功")

# CSVダウンロード実行
browser.find_element_by_xpath("//img[@alt='CSVダウンロード']").click()

# ログアウト実行
browser.find_element_by_xpath("//img[@alt='ログアウト']").click()

# 処理終了
browser.quit()

print("=== All done! ===")
```