execute_scriptなら画面外の要素をクリックできる


# 概要

通常Seleniumで要素をクリックする際は、
```Python
driver.find_element(By.XPATH, **** ).click()
```
でクリックすることができるのですが、
画面外の要素をクリックする際に、エラーが起きることがあったので、以下の記述に書き換えてたところ解決しました。

```Python
clickElement = driver.find_element(By.XPATH, ****)
driver.execute_script("arguments[0].click();", clickElement)
```

## 経緯
社内で使っているグループウェアで、データのバックアップがシステムデータのフルパッケージでしか取れないために、保存しているファイルを間違って消したり、階層フォルダごと消したりした際に、ファイル単位で復元することができないことが問題になり、どうにか自動的にファイル単位でバックアップが取れないか相談されたため、無理やり作った際のメモ。
正直クソ案件でしたが、こういう動的なシステムでも取得できることがわかったので良しとします。
システム導入の際は、この辺のことはチェックして導入しましょう。

## 環境
Python3.11
selenium 4.6.0
ChromeDriver（[お使いのGoogleChromeのVerに合わせてダウンロード](https://chromedriver.chromium.org/downloads)）

# 内容
実際に実行した内容を説明します。
ご興味あれば、読んでってください。

## 対象のシステム
グループウェアの書類やファイルを共有する機能から実際のファイルを取り出します。
しかし、階層化しているフォルダ構成を開かないと下位の要素をクリックできない上に`CLASS_NAME`で値を取得することがほぼできない困ったシステムでした。
`XPATH`で取得するしかない部分が多くて泣きました。

### 画面イメージ
ちょっと見にくいかもしれませんが、概ねニュアンス伝わればと思います。

**画面1**
![スライド1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416185/8fc9ff26-0123-d26d-937f-19223860d3d4.png)
この画面では、左側の階層化しているフォルダ構成とその中身のファイルが格納されたシートが表示されます。
最初に画面を開いた際には、フォルダ構成は閉じているのがデフォルトなので、クリックするためには開いて表示させる必要があります。
右下に表示している件数が出ていますが、これがあったおかげでなんとかfor文で回せました。

**画面2**
![スライド2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416185/1ad92df1-fb77-486c-350b-1e3b1a831ede.png)
この画面は、ファイルが格納されたシートを開いた画面です。
格納されたファイルを一括ダウンロードできるのですが、毎回ZIP化しているようで、その処理時間がものによって違うため、本当に厄介でした。

## 処理の流れ
ややこしいですが、以下のような流れになります。

1. 画面1のフォルダ構成は最初に全て展開させるように処理をする
2. 対象のフォルダ構成をクリックする　　 `繰り返す`
3. 表示されたシートの件数を読み込む
4. 1つずつシートをクリック 　　　　　　 `繰り返す`
5. 画面2にて、ファイルの一括ダウンロードをクリック
6. 表示されたらダウンロードをクリック
7. 閉じるをクリック
8. 戻るをクリック
9. `4以降を繰り返し`、3で読み込んだ件数分終わるまで続ける
10. `2から繰り返す`

1については特にコードは書きません。クリックの処理で開いておきます。

## 実際のコード

最初に定義しておきます。
`row=要素の位置`は、フォルダ構成の上位部分が何番目なのか定義するものです。順番が変わることがあってもここで変えてあげればいいというものです。
`k=フォルダ構成の個数`はフォルダ構成内の個数を取得する方法がなかったので、手動で無理やり値を入れています。

```Python
listCount = '//*[@id="listfrm"]/div[3]/div[2]/div'
zipPath =  '//*[@id="dispfrm"]/div[1]/div[2]/div[2]/div/div[2]/div/div[1]/div[2]/div[1]/a'
closeBtn = '//*[@id="neodialog-message"]/div[3]/div/button'
backBtn = '//*[@id="dispfrm"]/div[1]/div[1]/div/a'
# 
openA = '//*[@id="listfrm"]/div[2]/div[2]/div[3]/table/tbody/tr['
openB = "]/td[3]/a"
row = 9  # 要素の位置
k = 6  # フォルダ構成の個数

```

実際に走る処理としては、以下のようになります。
対象のシートをクリックするところで、フォルダ内のシートがたくさんある場合、画面外にあることがあり、エラーが出たので書き換えました。

```Python
# フォルダ構成下位で処理繰り返す
for j in range(1, k + 1):
    open = (
        '//*[@id="listfrm"]/div[2]/div[1]/div[3]/div[1]/ul/li/ul/li['
        + str(row) # 上位の階層ごとにしてしておく。
        + "]/ul/li[2]/ul/li[2]/ul/li["
        + str(j)
        + "]/a"
        # ここの記述が階層によって違うので取得して、繰り返しで使えるように加工
    )
    # 対象のフォルダ構成をクリック
    driver.find_element(By.XPATH, open).click()
    time.sleep(2)
    # 対象のフォルダ構成内のシートの件数を取得
    listCountNumber = driver.find_element(By.XPATH, listCount).get_attribute(
        "textContent"
    )
    CountNumber = listCountNumber.strip("全 件")
    n = int(CountNumber)

    # シート内の取得処理を繰り返す
    for i in range(1, n + 1):
        # 対象のシートをクリック
        open = openA + str(i) + openB
        clickElement = driver.find_element(By.XPATH, open)
        driver.execute_script("arguments[0].click();", clickElement)
        time.sleep(2)
        # 「ファイルを一括ダウンロード」をクリック
        driver.find_element(By.XPATH, zipPath).click()
        time.sleep(10)
        # 「ZIPダウンロード」をクリック
        driver.find_element(By.CLASS_NAME, "co-attzip-download").click()
        time.sleep(1)
        # 「閉じる」をクリック
        driver.find_element(By.XPATH, closeBtn).click()
        time.sleep(2)
        # 「戻る」をクリック
        driver.find_element(By.XPATH, backBtn).click()
        time.sleep(2)
        if i == n: # 件数分の処理後に繰り返し終了
            break
    if j == k: # フォルダ構成の個数分の処理後に繰り返し終了
        break
```


# まとめ

execute_scriptでのクリックについて、書きました。
とりあえず、クリックなどの要素を選択して実行する処理でエラーが出て、画面外の要素を選んでいる際は、1度書き換えて解消するか見てみてください。

```Python
clickElement = driver.find_element(By.XPATH, ****)
driver.execute_script("arguments[0].click();", clickElement)
```

## 参照

https://qiita.com/ba--shi/items/ca7a4d0525d6ea32ebe7

https://zenn.dev/moimoi_prog/articles/08467cf56f96d6