"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

Webアプリケーションへの自動入力・選択のTipsあれこれ/Selenium&python

Seleniumpythonの組合せで、Webアプリケーションを自動化して使う「個人的なTips」を3回続き物で書こうかなと思ってます。

今回は1回目の「入力」「選択」なんかに関わる部分です。

f:id:arakan_no_boku:20190914133925j:plain

 

はじめに

 

seleniumを使って、Webアプリケーションをpythonプログラムから自動制御する方法について書きます。

主な内容としては。

  • 書こうと思ったきっかけと3回にわけた構成
  • 記事を書く上での前提事項について

を書いて、あと、本文部分で

  • inputタグ(text、password)等への入力方法
  • 半角カタカナなど文字化けするものへの対応
  • jQueryのDatePickerへの入力方法
  • Selectでの選択方法

などについて書いてます。

 

3回にわけて書いていきます

 

自分は、seleniumを良く使います。

ただ、汎用的なツールとして活用している・・わけではありません。

例えば、Webアプリケーションのテストで何回も繰り返す面倒なところだけ部分的に自動化して「個人的に」使っている感じです。

そんな使い方でメモってきたTips的なものをまとめてみます。

内容的に「自分がよく使う」ケースに偏ってますが、似たような使い方をする誰かがいれば、参考にしてもらえたらいいな・・というスタンスです。

さて、はじめます。

ただ、長くなるので、3回にわけます。

  • 1回目:入力したり、選択したりのTipsあれこれ(今回)
  • 2回目:何かをクリックする場合のTipsあれこれ(9/24火曜日公開予定)
  • 3回目:Webアプリの値を参照したり、スクロールしたり他Tips(9/26木曜日公開予定)

今回は、1回目です。

 

シリーズ共通の前提

 

MyRpaToolsというクラスにメソッドを部品として作っていく感じでやっています。

使う時には、MyRpaToolsのインスタンスを生成して、操作の流れにあわせて、ペタペタと部品を貼っていく感じです。

使い方のイメージはこうです。

import my_rpa_tools as my
from selenium import webdriver
import time


# Edge WebDriver initial setting
driver = webdriver.Edge(
    executable_path='C:\\windows\\system32\\MicrosoftWebDriver.exe')
driver.get('https://coopweb.itecs.local/pro_staff/ps_login.php')
auto = my.MyRpaTools(driver)
# login
auto.input_text('login_id', 'user0001')

クラスは「my_rpa_tools.py」に定義しています。

上記例だと、Edgeドライバーですが、いったん「driver」オブジェクトをセットして、それを引数にして「MyRpaTools」クラスインスタンスを生成します。

上記ではその名前を「auto」にしていて、あとは、「auto.input_text」のように、クラスで定義したメソッドを並べていく感じです。

なので。

記事中のソースサンプルは、その部品となるメソッドをのせてます。

クラスの構造はこうです。

my_rpa_tools.py

from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait as wait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC


class MyRpaParts():
    def __init__(self, web_driver):
        self.driver = web_driver
        self.scroll_height = 0

初期化時に、self.driverにweb_driverをうけとって内部で保持し、それを各メソッドの中で使っていきます。 

セレクタは「name」を使ってます。

ただ、一部jQueryを使っているのですが、その場合だけIDで指定しています。

理由は自分のよく使う環境だと、それが適切だから・・(笑)です。

なお、定義したクラスのソース全体は3回目の末尾にのせる予定です。

 

個別の入力部品群

 

ここからは、個別の入力部品の定義を書いていきます。

 

まずは普通のテキストボックスへの入力

 

普通の「<input type="text"  name="text001" value="" />」に対応するものです。

    def input_text(self, name, value):
        element = self.driver.find_element_by_xpath(
            "//input[@name='" + name + "'][@type='text']")
        element.send_keys(value)

普通にsend_keysを使ってます。

使い方は。

auto.input_text('login_id_name', 'user0001')

みたいな感じです。

実際には同じようなテキストボックスでも、typeがnumberだったりpasswordだったりしますので、以下のような定義も作っておきます。

    def input_number(self, name, value):
        element = self.driver.find_element_by_xpath(
            "//input[@name='" + name + "'][@type='number']")
        element.send_keys(value)

とか 

    def input_password(self, name, value):
        element = self.driver.find_element_by_xpath(
            "//input[@name='" + name + "'][@type='password']")
        element.send_keys(value)    

とかですね。

これにはいくつかの制限事項があり、大きなものは以下の2つです。

  • 半角カタカナの入力が文字化けしてしまう。
  • 画面上から隠れているとエラーになる

 

半角カタカナの入力を文字化けさせない

 

これはSeleniumだけではできません。

以下のように、JavaScriptを直接使って部品を定義します。

    def input_by_name(self, name, value):
        self.driver.execute_script(
            "document.getElementsByName('" + name + "')[0].value = '" + value + "';")

この方法を使うと、さきほどsend_keysの制約にしていた部分をクリアできます。

もちろん、半角カタカナの文字化けも回避できます。

汎用敵なな名前のままだと、自動化対象の画面とつきあわせる時にわかりづらいので、これを内部で使って以下のような半角カタカナ専用メソッドを定義して使ってます。

    def input_text_kana(self, name, value):
        self.input_by_name(name, value)

jQueryが使えそうな環境なら、こんなのも使ってます。

    def input_direct_by_id(self, id, value):
        self.driver.execute_script(
            "$('#" + id + "').val('" + value + "');")    

 

ちょっとした工夫として、jQueryを内部で使っているのがわかるように、idをセレクタにして、nameをセレクタにしているプレーンJavaScriptのメソッドと区別しています。

今のところ、自分がやっている環境では、この程度で、テキスト入力については、ほぼほぼ「困ったこと」を解消できてます。

 

jQueryのDatepickerに入力する

 

自分が自動化対象にしているWEBアプリケーションでは、jQueryのDatePickerも使われているので、対応しています。

これは、JavaScriptでコントロールされていて、操作に従ってカレンダーが表示されたりするので、結構自動操作すると面倒なものです。

二通りの方法を使ってます。

ひとつは、普通に操作して当月の日付を選択入力するものです。

    def input_datepicker_current_month(self, name, select_day):
        self.driver.find_element_by_name(name).click()
        wait(self.driver, 10).until(EC.visibility_of_element_located(
            (By.XPATH, "//td[@data-handler='selectDay']/a[text()='" + select_day + "']"))).click()

日付のテキストボックスをクリックして、カレンダーが表示されるのをまって、指定した日付の部分を選択してます。 

でも。

これだと、当月以外の日付を入力するのが、とても面倒です。

生年月日みたいな30年前の日付とか言われると「地獄(笑)」です。

なので、日付を直接入力します。

    def input_datepicker_dairect(self, id, value):
        self.driver.execute_script(
            "$('#" + id + "').datepicker().datepicker('setDate','" + value + "');")

ここはjQueryを使っているので、nameではなく、idになっているので注意です。

使い方としては。

auto.input_datepicker_dairect('date_pick_id', '1988年9月1日(木)')

とか  

auto.input_datepicker_dairect('date_pick_id', '1988/09/01')

 のように指定の日付を直接入力します。

注意としては、日付のフォーマットです。

普通、設定しているjQueryのソースなんか見れないので、手入力で日付入力してみて結果として表示されるフォーマットで入力すれば問題ないですが、フォーマットが違うと変な日付になるので注意が必要です。

 

複数行入力はごくシンプル

 

次は複数行入力(textarea)です。

普通にSeleniumの機能を使うなら、こんな感じ。

    def input_textarea(self, name, value):
        element = self.driver.find_element_by_xpath(
            "//textarea[@name='" + name + "']")
        element.send_keys(value)
    

もちろん、send_keys を使っているので、半角カタカナとかの制約は同じです。

回避するには、Textと同じようにJavaScriptを直接よぶパーツを使うことになりますが、今のところ、半角カタカナを複数行入力するようなケース(そんなのあるのか?)は見たことがないので、用意してません。

 

Selectで選択する

 

Selectに関しては、Seleniumの機能をそのまま使うものしか用意してません。

特に困ってないので。

valueの値で選択するものです。

    def select_by_value(self, name, value):
        element = self.driver.find_element_by_name(name)
        element_select = Select(element)
        element_select.select_by_value(value)
    

オプションの 選択肢の「value」の値を確認して指定します。

Selectでよくあるパターンで、例えば、都道府県と市区町村とか、なんかの種別を選んで下に選択肢が表示される・・みたいな、Ajax的な動きのものがあります。

これを何気にやっていると、都道府県を選んで、市区町村の選択肢が更新されるまえに読みに行ってエラーになる・・なんてことが、よく発生します。

こんなふうに、上の段の値によって、選択値が変更される場合は、下の段の更新がちゃんと終わるまで待ってやらないといけません。

そんな時は。

auto.select_by_value('parent_kind', '4')
time.sleep(1)
auto.select_by_value('child_type', '8')    

こんな感じで間にsleepとかはさんで待ってやるか、 waitとかを使って下段が表示されるまで待つみたいにします。

waitを使う方がそれっぽいですが、自分はsleep()を好んで使ってます。

まず、わかりやすいです。

それに、1秒位の間があいた方が、自動で動いている画面を見ている時に見やすいということもあります。

スタンスとしては、それで遅いとストレスを感じたりするようになったら、その部分だけwaitとか使ってコントロールすればいいや・・って感じですね。

 

第一回目まとめ

 

入力・選択については、今のところ、こんなものです。

使いながら、新たなものがでてきたら、追加していきます。

seleniumのインストールや、Webドライバの設定などは、こちらの記事でまとめているやり方で環境をつくってます。

arakan-pgm-ai.hatenablog.com

続きは次回で。

ではでは。