SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

WebアプリケーションRPA(1):自動的に入力・選択する部品/Pythonサンプル

f:id:arakan_no_boku:20190914133925j:plain

目次

前提

Python3とSeleniumを使います。

今、かかわっているシステムで使われているため、一部でjQueryを使っています。

インストール環境設定はできている前提で話をすすめます。

まだの場合は、こちらでインストールと環境設定を行う必要があります。

arakan-pgm-ai.hatenablog.com

記事は3回にわけて書いていきます

自分はSeleniumはよく使いますが、全面的な自動化は目指していません。

全面的な自動化は、自動化自体のテストや保守コストの負担が重いからです。

テストで何回も繰り返す面倒なところだけ部分的に素早く自動化する。

そのためのToolを作っていきます。

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

  • 1回目:自動的に入力・選択する部品(今回)
  • 2回目:自動的に何かをクリックする部品
  • 3回目:自動的な値の参照や、スクロールなどの部品

今回は、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://local/pf/pn.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セレクタにしています。

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

以下、個別の入力部品の定義を書いていきます。 

入力部品1:のテキストボックスへの入力 

普通の「<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つです。

  • 半角カタカナの入力が文字化けしてしまう。
  • 画面上から隠れているとエラーになる
入力部品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 + "');")    

 

 

今のところ、自分がやっている環境では、この程度で、テキスト入力については、ほぼほぼ対応できてます。 

入力部品3: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のソースなんか見れないので、手入力で日付入力してみて結果として表示されるフォーマットで入力すれば問題ないですが、フォーマットが違うと変な日付になります。 

入力部品4:複数行入力

複数行入力(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を直接よぶパーツを使うことになりますが、今のところ、半角カタカナを複数行入力するようなケースは見たことがないので、用意してません。 

入力部品5: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秒位の間があいた方が、自動で動いている画面を見ている時に見やすいということもあります。

スタンスとしては、まずsleep()でやっておいて、それでストレスを感じる部分だけwaitを使って細かくコントロールすればいいと思っています。

 

二回目・三回目の記事へのリンク 

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

3回にわけている2回目、3回目はこちらです。

2回目

arakan-pgm-ai.hatenablog.com

3回目

arakan-pgm-ai.hatenablog.com

seleniumのインストールや、Webドライバの設定は、こちらの記事でまとめています。

arakan-pgm-ai.hatenablog.com

続きは次回で。

ではでは。

#Python