PyInstallerとSleniumのfind_element_by_css_selector超便利

こんばんは、@0yanです。
課メンバーに業務自動化要望をヒアリングしたら、「Webアプリの手作業を自動化して欲しい」という要望があった為、Seleniumで自動化ツールを作成、PyInstallerで実行ファイルにして配布しました。
その際、学びがあったのでこちらで紹介したいと思います。



# 想定している読者
- Python＋Seleniumで業務自動化しているノンプログラマーや初心者の方
- コード読めない方から「Webアプリの自動化ツールを作って欲しい」と要望受けている方



# コンテンツ
- PyInstaller超便利
- Seleniumのfind_element_by_css_selector超便利
- おまけ：Seleniumで躓きがちなこと



# 環境
- Windows10
- Python 3.7.3（Anaconda3）



# PyInstaller超便利
自動化ツール依頼元のメンバーがプログラミング未経験者のため、「環境構築だけで一苦労だろうな・・・どうにか良い方法ないだろうか」と探しておりました。
ありました。
実行ファイル形式で配布するという方法が。

#### 使い方の前に注意事項
普段、Seleniumを使った自動化をする際は、[chromedriver-binary](https://pypi.org/project/chromedriver-binary/)を使った方がラクなので、そうしている方が多いと思います。
が！Seleniumを使ったコードを実行ファイル形式にして配布しても、Chrome Driverが無ければ動きません。

- Chrome Driverは実行ファイル形式を使うこと前提でコーディングする
- 実行ファイルとセットでChrome DriverのダウンロードURLも共有、適切なドライバーをダウンロードするよう伝える
- Chrome Driverの実行ファイルは指定のパスに格納することを伝達する
- 配布先のPCのデスクトップパス等は、`os.environ.get()`を使って取得するようにする
- ログインIDやパスワードの入力を求める際は、input関数を使って最初に求めるようにする

といった配慮が必要です。

#### PyInstallerの使い方
1. `pip install pyinstaller`コマンドでPyInstallerをインストール
2. `pyinstaller ファイル名.py --onefile`コマンドを打ち、実行ファイルを作成
3. distディレクトリに実行ファイルができるので配布する

以上、3stepです。
めっちゃ簡単！



# Seleniumのfind_element_by_css_selector超便利
今まで、Seleniumで要素を探す際、
- find_element_by_id
- find_element_by_name
- find_element_by_class_name
- find_element_by_tag_name
- find_element_by_link_text
- find_element_by_partial_link_text
でダメだったら「詰んだ・・・最後の手段だ！」とfind_element_by_xpathメソッドを使っておりましたが、**find_element_by_css_selectorメソッドを使えば解決できる**ということを知りました。
※ここから先、`find_element_by_`は`～`で省略させて頂きます。

#### 使い方①：クラス名の中に空白があっても大丈夫！
HTMLタグ内のクラス名が「class=test hogehoge」となっている場合、～class_nameメソッドでは対応できませんが、**～css_selector(.test.hogehoge)**とすることで要素取得できます。

#### 使い方②：クラス名が重複してても大丈夫！
例えば、hogehogeというクラス名のHTMLタグが複数あった場合、find_elements_by_class_nameメソッドでそれらをすべて取得、for文で一つずつ目的の要素か否か調べることもできますが、～css_selectorメソッドの引数を**タグ名.クラス名**や**タグ名#ID名.クラス名**とすることでユニークになれば、for文まわさなくてもいけます。

```
【具体例1：下記タグが同じページに存在する場合】
1) <select class=hogehoge>
2) <input class=hogehoge>
3) <input id=123 class=hogehoge>
4) <input class=hogehoge name=789>

≪要素取得方法≫
1) find_element_by_css_selector('select.hogehoge')
2) find_element_by_css_selector('input.hogehoge')
3) find_element_by_css_selector('input#123.hogehoge')
4) find_element_by_css_selector('input.hogehoge[name="789"])


【具体例2：divの属性が異なるが、その直下のタグのクラス名や属性がまったく同じ場合】
1) <div class="pb-edit" data-id="1628">の子の<select>タグ
2) <div class="pb-edit" data-id="1629">の子の<select>タグ

≪要素取得方法≫
1) driver.find_element_by_css_selector('div[data-id="1628"] > select)
2) driver.find_element_by_css_selector('div[data-id="1629"] > select)


【具体例3：定期的にクラス名が変わって困る場合】
≪タグ≫
<button placeholder="メンバーを検索" value="" class="Button__DefaultButton-ouh7gw-1 Button__PrimaryButton-ouh7gw-2 SearchBox__SearchButton-sc-1riae5a-1 bHQZyE Button__PrimaryMediumButton-ouh7gw-4 cFhiOs">

≪要素取得方法≫
下記の通り、～css_selector('タグ名[属性名="値"]')でいける。
find_element_by_css_selector('button[placeholder="メンバーを検索"]')

```

多分、普段CSSを触っている方からしたら「こんな簡単なことも知らんのかい！」と言われるような内容かもしれません。
が、私のようにノンプログラマーでCSSと縁がない人間でも便利に使えるのがPythonの良さだと思いますし、そういう方もいると思いますのでここで共有させて頂きました。



# おまけ：Seleniumで躓きがちなこと
ファイルアップロードとスクロールで躓きがちだと思います。
知っている方もいるかと思いますが、こちらで紹介させて頂きます。

#### ファイルアップロード
Send_keysでできます。

#### スクロール
画面に表示されていない要素は取得できないため、スクロールしたいこともあると思います。
下記のとおり、ActionChainsクラスを使うことでスクロールできます。

```python
element = driver.find_element_by_id('hogehoge')
action = ActionChains(driver)
action.move_to_element(element)
action.perform()
```



# さいごに
長文にも関わらず、ご覧頂き誠にありがとうございました！
もし、間違い等がございましたらご指摘頂けますと幸いです。
宜しくお願い致しますm(_ _)m



# 参考サイト
> [PythonスクリプトをWindows環境で動くexeファイルにしよう！](https://www.sejuku.net/blog/65881)
> [コマンドプロンプト/Windowsの主要な環境変数一覧と意味](https://win.just4fun.biz/?%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89%e3%83%97%e3%83%ad%e3%83%b3%e3%83%97%e3%83%88/Windows%e3%81%ae%e4%b8%bb%e8%a6%81%e3%81%aa%e7%92%b0%e5%a2%83%e5%a4%89%e6%95%b0%e4%b8%80%e8%a6%a7%e3%81%a8%e6%84%8f%e5%91%b3)
> [【初心者向け】CSSセレクタとは？セレクタの種類や指定方法を解説！（基礎編）](https://www.asobou.co.jp/blog/web/css-selectors)
> [セレクタの種類](http://www.htmq.com/csskihon/005.shtml)
>[Python3 selenium ローカルファイル選択](https://teratail.com/questions/133658)