Selenium で ZIA Admin Portal の操作を自動化してみる（ログをダウンロードする編）

# 1. はじめに

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

この記事は[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 で ZIA Admin Portal の操作を自動化してみる（ログイン編）](https://qiita.com/Shojiro_Otani/items/c22c1725b0a53dd3e492)
[Selenium で ZIA Admin Portal の操作を自動化してみる（URL Lookup編）](https://qiita.com/Shojiro_Otani/items/3654ae95776e47df4950)


# 2. ZIA Admin Portal からログを参照するには？

ZIA のログは Insight Logs から参照することができます。詳細は弊社の Help か SB C&S 様のブログをご覧ください。

https://help.zscaler.com/ja/zia/about-insights-logs

https://licensecounter.jp/engineer-voice/blog/articles/20230906_zscalerzia.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/ad2d872f-9888-e452-73a1-933dc29e63fe.png)

> ZIA Help - [解析ログについて](https://help.zscaler.com/ja/zia/about-insights-logs) より


# 3. Selenium を使って ログのダウンロードを自動化してみる

### 設定

今回は URL フィルターによってブロックされたアクセスをダウンロードすることにします。フィルター項目に「Blocked Policy Type」（トランザクションをブロックしたポリシーの種類）を、フィルター条件に「URL Filtering」を選択します。

```python
#
#
# ユーザ設定
#
#
# ZIA クラウドの名前
cloud_name = '<環境に合わせて設定>'
#
# ログインユーザ名
username = '<環境に合わせて設定>'
#
# フィルター項目と条件
filter_item = "Blocked Policy Type"
filter_condition = "URL Filtering"
```


### ブラウザの起動、ZIA Admin Portal へアクセス、ログイン

こちらについては第１回目の記事で解説しています。
[Selenium で ZIA Admin Portal の操作を自動化してみる（ログイン編）](https://qiita.com/Shojiro_Otani/items/c22c1725b0a53dd3e492)

### Insights Logs のページへアクセスする

リンクをクリックするよりは直接 URL を叩いたほうが早いのでそうします。

```python
# Insights へ移動
driver.get(f"https://admin.{cloud_name}/#insights/web") 

# Insights Logs へ移動
insights_logs = WebDriverWait(driver, 60).until(
    EC.element_to_be_clickable((By.ID, "tab-LOGS-Id"))
)
insights_logs.click()
```

Insights Logs へ移動しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/891b482e-89a6-df6a-aefb-f8a49d721724.png)


### フィルターと時間帯を設定する

ここでは予想外に長いコードになっていますが、ドロップダウンメニューから選択をしていると途中からログの検索が走ってしまい、次のクリックができなくなることがありましたので、```EC.element_to_be_clickable``` で要素がクリックできる状態になるまで待機するようにしています。

また Timeframe（時間帯）は最後に設定したほうが動作が安定するためそうしています。

まずはフィルター項目として Blocked Policy Type をセットします。

```python
# Add Fiter からフィルター項目のドロップダウンを開く
add_filter_item_button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, "//span[text()='Add Filter']"))
)
add_filter_item_button.click()

# フィルター項目を検索
filter_item_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CLASS_NAME, 'search-input-text'))
)
filter_item_element.send_keys(filter_item)

# フィルター項目が一番上に表示されるまで待機
wait = WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.dropdown-list-item.-js-dropdown-list-item:nth-child(1)'), filter_item)
)

# 検索に一致した一番上のフィルター項目を選択
filter_item = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".-js-dropdown-list-item:nth-child(1)"))
)
filter_item.click()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/af3240b0-d6b2-2b51-64fe-7fd606a46ccf.png)

次にフィルター条件として URL Filitering をセットします。

```python
# フィルター条件のドロップダウンを開く
add_filter_condition_dropdown = WebDriverWait(driver, 30).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".filter .dropdown-button"))
)
add_filter_condition_dropdown.click()

# フィルター条件を検索
filter_condition_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".focus-visible"))
)
filter_condition_element.send_keys(filter_condition)

# フィルター条件が一番上に表示されるまで待機
wait = WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.-js-dropdown-list-item:nth-child(1)'), filter_condition)
)

# 検索に一致した一番上のフィルター条件を選択
filter_condition = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".-js-dropdown-list-item"))
)
filter_condition .click()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/84e9aca9-249c-5ae0-66e9-237497050ebe.png)

次に、CSV ダウンロードを選択します。

```python
# CSVダウンロードをクリック
csv_download_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".insights-options-panel:nth-child(1) .button-radio-button:nth-child(1)"))
) 
csv_download_element.click()
```

次に Timeframe（時間帯）をセットします。ここでは先月（Previous Month）をセットします。

```python
# Timeframのドロップダウンを開く
timeframe_button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".dropdown-button"))
)
timeframe_button.click()

# "Previous Month" を選択
timeframe_option = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@class='dropdown-list-old']//div[text()='Previous Month']"))
)
timeframe_option.click()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/d03422ef-275e-c80e-bdf7-e47823c91366.png)

※ Insights Logs ではカレンダーから任意の日時範囲を設定することができます。Selenium から JavaScript を実行して日時範囲をセットできそうなのですが、今回は諦めました・・・。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/20f3cd7b-c201-66bc-8721-1e31e76e35e1.png)

最後に Apply Filters を選択して CSV ダウンロードを開始します。ログの検索にかかる時間が読めないため、ダウンロードの進捗バーが表示されているあいだは無制限に待機します。

```python
# Apply Filters を選択して CSV ダウンロード開始
apply_filters_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".insights-options-filter-buttons"))
)
apply_filters_element.click()

# CSVダウンロードが開始された後、進捗バーが非表示になるまで待機
wait = WebDriverWait(driver, float("inf"))
wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "progress-bar-holder")))
```


### デモ

では、スクリプトを実行してみましょう。ログイン部分は[第1回目の記事](https://qiita.com/Shojiro_Otani/items/c22c1725b0a53dd3e492)をご覧ください。

フィルターを設定している途中でなぜか画面右側でログの検索が始まってしまっていますが、なんとか回避しています。

![download_logs.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622818/da09486d-85c7-81e3-5183-1fefd8fac902.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
from selenium.webdriver.support.ui import Select
#
#
# ユーザ設定
#
#
# ZIA クラウドの名前
cloud_name = '<環境に合わせて設定>'
#
# ログインユーザ名
username = '<環境に合わせて設定>'
#
# フィルター項目と条件
filter_item = "Blocked Policy Type"
filter_condition = "URL Filtering"
#
#
# 処理の開始
#
#
# Prompt for password and receive hidden input / パスワードの入力を促すプロンプトを表示し、入力を非表示で受け取る
password = getpass.getpass("Enter your password: ")

# ウェブブラウザの起動
driver = webdriver.Chrome()

# ブラウザのウィンドウサイズをセット
driver.set_window_size(1064, 910)

# 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 する
banner_element = WebDriverWait(driver, 120).until(
    EC.presence_of_element_located((By.CLASS_NAME, "notification-message-container"))
)
if banner_element.is_displayed():
    # Close ボタンをクリック
    close_banner_button = banner_element.find_element(By.CLASS_NAME, "notification-close-icon")
    close_banner_button.click()

# ポップアップが出たら Close する
popup_element = WebDriverWait(driver, 120).until(
    EC.presence_of_element_located((By.CLASS_NAME, "dialog-mask-splash"))
)
if popup_element.is_displayed():
    # Close ボタンをクリック
    close_popup_button = popup_element.find_element(By.CLASS_NAME, "-js-cancel-button")
    close_popup_button.click()

time.sleep(1)

# Insights へ移動
driver.get(f"https://admin.{cloud_name}/#insights/web") 

# Insights Logs へ移動
insights_logs = WebDriverWait(driver, 60).until(
    EC.element_to_be_clickable((By.ID, "tab-LOGS-Id"))
)
insights_logs.click()

# Add Fiter からフィルター項目のドロップダウンを開く
add_filter_item_button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, "//span[text()='Add Filter']"))
)
add_filter_item_button.click()

# フィルター項目を検索
filter_item_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CLASS_NAME, 'search-input-text'))
)
filter_item_element.send_keys(filter_item)

# フィルター項目が一番上に表示されるまで待機
wait = WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.dropdown-list-item.-js-dropdown-list-item:nth-child(1)'), filter_item)
)

# 検索に一致した一番上のフィルター項目を選択
filter_item = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".-js-dropdown-list-item:nth-child(1)"))
)
filter_item.click()

# フィルター条件のドロップダウンを開く
add_filter_condition_dropdown = WebDriverWait(driver, 30).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".filter .dropdown-button"))
)
add_filter_condition_dropdown.click()

# フィルター条件を検索
filter_condition_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".focus-visible"))
)
filter_condition_element.send_keys(filter_condition)

# フィルター条件が一番上に表示されるまで待機
wait = WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.-js-dropdown-list-item:nth-child(1)'), filter_condition)
)

# 検索に一致した一番上のフィルター条件を選択
filter_condition = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".-js-dropdown-list-item"))
)
filter_condition .click()

# CSVダウンロードをクリック
csv_download_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".insights-options-panel:nth-child(1) .button-radio-button:nth-child(1)"))
) 
csv_download_element.click()

# Timeframのドロップダウンを開く
timeframe_button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".dropdown-button"))
)
timeframe_button.click()

# "Previous Month" を選択
timeframe_option = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@class='dropdown-list-old']//div[text()='Previous Month']"))
)
timeframe_option.click()

# Apply Filters を選択して CSV ダウンロード開始
apply_filters_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, ".insights-options-filter-buttons"))
)
apply_filters_element.click()

# CSVダウンロードが開始された後、進捗バーが非表示になるまで待機
wait = WebDriverWait(driver, float("inf"))
wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "progress-bar-holder")))

# 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. 最後に

Selenium で ZIA Admin Portal の操作を自動化してみる、３回シリーズは今回で終了です。Selemium というオープンソースを利用することに高いハードルがあるとは思いますが、みなさまの運用の自動化・効率化に対する気づきとなれば幸いです。

それでは良いお年をお迎えください。