Selenium で ZIA Admin Portal の操作を自動化してみる（ログイン編） 

# 1. はじめに

みなさん、こんにちわ。ゼットスケーラーの大谷です。ゼットスケーラーではカスタマーサクセスマネージャとして、お客様が弊社のサービスを活用いただくことでゼットスケーラーのサービスの価値を感じていただけるように様々なご支援を担当しています。

お客様へのご支援のなかには日々の運用における課題をお聞きし、その解決に向けた検討をお客様と一緒に進めさせていただいています。そのなかで最近は運用の自動化にも取り組んでおり、例えばゼットスケーラーのサービスに実装されている API についてご紹介していたりします。

**Zscaler の主なサービスの API**

- [ZIA API](https://help.zscaler.com/zia/getting-started-zia-api)
- [ZPA API](https://help.zscaler.com/zpa/understanding-zpa-api)
- [ZDX API](https://help.zscaler.com/zdx/understanding-zdx-api)

なお、この記事は[Zscaler Advent Calendar 2023](https://qiita.com/advent-calendar/2023/zscaler)というお祭りイベント向けに「遊んでみた」系の内容にしてみようということで、Selenium で ZIA Admin Portal を操作してみようと思います。

https://qiita.com/advent-calendar/2023/zscaler

今回は ZIA Admin Portal へのログイン・ログアウトを Selenium で操作してみますが、次回はもう少し複雑な操作方法について書きたいと思います。

# 2. Selenium とは？

[Selenium](https://www.selenium.dev/ja/) とはウェブブラウザの操作を自動化するためのフレームワークとして開発されているオープンソースソフトウェアです。[Python](https://www.python.org) などを使用してウェブブラウザの操作をプログラム化させることができます。

![selenium-logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/50adca4e-81a6-0ff2-c1f6-5dcbea9d8a26.png)


# 3. ZIA Admin Portal とは？

ZIA（正式名：[Zscaler Internet Access](https://www.zscaler.jp/products/zscaler-internet-access)） Admin Portal はインターネットとSaaSへのセキュアなアクセスを提供する Zscaler Internet Access のポリシー設定などを企業のIT管理者が管理するためのウェブベースのポータルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/a2d00e9a-3df5-e970-ed7d-ceea366399f9.png)
> ZIA Help - [ZIA Admin Portalへのアクセスとナビゲート](https://help.zscaler.com/ja/zia/accessing-and-navigating-zia-admin-portal)より

# 4. Selenium を使って ZIA Admin Portal にログインしてみる


### Python と Selenium のインストール

公式・非公式問わず、インターネットに情報が豊富にありますので、そちらを参照してみてください。

- [Windows版Pythonのインストール](https://www.python.jp/install/windows/install.html)(「Python.jp プログラミング言語 Python情報サイト」より)
- [Seleniumライブラリのインストール](https://www.selenium.dev/ja/documentation/webdriver/getting_started/install_library/)(Selenium公式サイトより)

### 設定、ブラウザの起動、そして ZIA Admin Portal へアクセスする

最初にモジュールのインポートと設定を行い、ブラウザを起動して ZIA Admin Portal へアクセスします。

```python
import time
import json
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
import getpass
from selenium.common.exceptions import TimeoutException
#
#
# ユーザ設定
#
#
# ZIA クラウドの名前
cloud_name = '<環境に合わせて設定>'
#
# ログインユーザ名
username = '<環境に合わせて設定>'
#
# Prompt for password and receive hidden input / パスワードの入力を促すプロンプトを表示し、入力を非表示で受け取る
password = getpass.getpass("Enter your password: ")
#
#
# 処理の開始
#
#
# ウェブブラウザの起動
driver = webdriver.Chrome()

# ZIA Admin Portal へアクセス
driver.get(f"https://admin.{cloud_name}") 
```

### ログインする

Selenium ではウェブブラウザ上の入力フォームやリンクに該当する HTML のある部分を「要素」として定義し、そこに文字の入力やクリックといったアクションを実行するようにプログラムを組みます。

ZIA Admin Portal のログイン画面のログインIDとパスワード、それにサインインのボタンは HTML で以下のようになっています。

```html
<div class="login-input-container">
    <div class="login-button-container">
        <div class="input-container">
            <span id="login-panel-label-email" class="login-text-label">Login ID</span>
            <div class="login-text-input">
                <input id="login-panel-input-email" class="login-text-input-text" type="email" required="required" tabindex="1" autofocus="autofocus" autocomplete="off" placeholder="Type Your Login ID..." data-keeper-edited="yes">
                ↑これがログインIDを入力するフォーム
                (省略)
            </div>
        </div>
        <div class="input-container">
            <span id="login-panel-label-password" class="login-text-label">Password</span>
            <div class="login-text-input active">
                <input id="login-panel-input-password" class="login-text-input-text" type="password" required="required" tabindex="2" autocomplete="off" placeholder="Type Your Password..." data-keeper-focused="true" data-keeper-edited="yes">
                ↑これがパスワードを入力するフォーム
                (省略)
            </div>
        </div>
        <div class="input-container login-signin-button">
            <span id="login-panel-signin-button" class="buttons big">Sign In</span>
            ↑これがサインインボタン
        </div>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/e5bccefe-117b-685f-2b17-3d5637bdb270.png)
> ZIA Help - [ZIA Admin Portalへのアクセスとナビゲート](https://help.zscaler.com/ja/zia/accessing-and-navigating-zia-admin-portal)より

この HTML に対して属性を指定して（今回の場合は id 属性にするので```By.ID```で）要素を定義し、文字の入力（```send_key```）やクリック(```click```)を実行します。


```python
# ログインIDとパワワード、サインインのボタンの要素を取得する
username_form = driver.find_element(By.ID, "login-panel-input-email")
password_form = driver.find_element(By.ID, "login-panel-input-password")
login_button = driver.find_element(By.ID, "login-panel-signin-button")

# 一旦入力フォームを空にする
username_form.clear()
password_form.clear()

# ログインIDとパスワードを入力する （username と password は別途定義する）
username_form.send_keys(username)
password_form.send_keys(password)

# サインインのボタンをクリックする
login_button.click()
```

### ポップアップを消す

ログイン後、新バージョンのリリースなどをお知らせするポップアップが表示されることがあります。このままにしておくと操作や表示に支障やがありますので、Close させたいと思います。（ここから先はHTMLソースの掲載を控えます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/6a92ebca-a7f0-c723-7096-d6b98021e48d.png)

```python
# ポップアップが出た場合は Close する
try:
    popup_element = WebDriverWait(driver, 60).until(
        EC.presence_of_element_located((By.CLASS_NAME, "dialog-mask-splash"))
    )
    
    # 要素が表示されているか確認
    if popup_element.is_displayed():
        # Close ボタンをクリック
        close_button = popup_element.find_element(By.CLASS_NAME, "-js-cancel-button")
        close_button.click()
        
except Exception as e:
    print(f"An error occurred: {e}")
```

### ログアウトしてスクリプトを終了する

最後にログアウトのボタンをクリックして、ブラウザを終了させてスクリプトを終了させます。


```python
# マウスオーバー対象の要素が表示され、クリックできるようになるまで待機
logout = WebDriverWait(driver, 60).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".fa-arrow-alt-from-right")),
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".fa-arrow-alt-from-right"))
)

# Logout ボタンをマウスオーバーさせて Tooltip を表示させる
action = ActionChains(driver)
action.move_to_element(logout).perform()

# 本来は不要ですが、このデモ向けに Logout の Tooltip を表示させるために待機
time.sleep(1)

# Logout ボタンをクリックする
logout.click()

# ウェブブラウザをクローズする
driver.quit()

```

### デモ

では、スクリプトを実行してみましょう。全体で３０秒くらいの動画です。

1. ブラウザを起動して ZIA Admin Portal へアクセス
2. ログイン ID とパスワードを入力して、サインインボタンをクリック
3. ポップアップが表示されたら Close ボタンをクリック
4. 最後に Logout ボタンをクリック

![login.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/035f62c3-0de5-626b-db71-f781f2893204.gif)

# 5. 今回のスクリプト

今回使用したスクリプトはこちらです。

```python
import time
import json
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
import getpass
from selenium.common.exceptions import TimeoutException
#
#
# User configuration / ユーザ設定
#
#
# ZIA cloud name / ZIA クラウドの名前
cloud_name = '<環境に合わせて設定>'
#
# Login user name / ログインユーザ名
username = '<環境に合わせて設定>'
#
# Prompt for password and receive hidden input / パスワードの入力を促すプロンプトを表示し、入力を非表示で受け取る
password = getpass.getpass("Enter your password: ")
#
#
# 処理の開始
#
#
# ウェブブラウザの起動
driver = webdriver.Chrome()

# ZIA Admin Portal へアクセス
driver.get(f"https://admin.{cloud_name}") 

# ログインIDとパワワード、サインインのボタンの要素を取得する
username_form = driver.find_element(By.ID, "login-panel-input-email")
password_form = driver.find_element(By.ID, "login-panel-input-password")
login_button = driver.find_element(By.ID, "login-panel-signin-button")

# 一旦入力フォームを空にする
username_form.clear()
password_form.clear()

# ログインIDとパスワードを入力する
username_form.send_keys(username)
password_form.send_keys(password)

# サインインのボタンをクリックする
login_button.click()

# ポップアップが出た場合は Close する
try:
    popup_element = WebDriverWait(driver, 60).until(
        EC.presence_of_element_located((By.CLASS_NAME, "dialog-mask-splash"))
    )
    
    # 要素が表示されているか確認
    if popup_element.is_displayed():
        # Close ボタンをクリック
        close_button = popup_element.find_element(By.CLASS_NAME, "-js-cancel-button")
        close_button.click()
        
except Exception as e:
    print(f"An error occurred: {e}")

# Logout ボタンが要素が表示され、クリックできるようになるまで待機
logout = WebDriverWait(driver, 60).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".fa-arrow-alt-from-right")),
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".fa-arrow-alt-from-right"))
)

# Logout ボタンをマウスオーバーさせて Tooltip を表示させる
action = ActionChains(driver)
action.move_to_element(logout).perform()

# 本来は不要ですが、Logout の Tooltip が表示されるのを確認するために待機
time.sleep(1)

# Logout ボタンをクリックする
logout.click()

# ウェブブラウザをクローズする
driver.quit()
```

# 6. 最後に

今回は簡単にログイン・ログアウトだけの操作でしたが、次回はもう少し複雑な操作をしてみたいと思います。