PythonでSeleniumを使い毎朝の業務を自動化させた

1. はじめに

私は業務で毎朝以下のルーティンを任されています
行程 : 使用するシステム : 業務内容 : 所要時間
① : Aシステム : Bシステムにデータを送信(大体40のデータ) : 10秒
② : Bシステム : 1データに対し1つのPDFを作成 : 3分
③ : Aシステム : 1データに対し1つのPDFを添付して送信 : 30分

Aシステムはブラウザ上(IE専用)、Bシステムはjavaで作成されたシステムです
図化すると下記のような感じ f:id:Atc:20180724185101j:plain (なんか透けてる)


①,②はすぐに終わるので良いとして、③はひたすらPDFを添付し続けるという苦行を30分間も強いられます。しかもAシステムは画面が遷移する度に「読み込み中」ポップアップが3秒程(長いときは5秒以上)表示されます。勉強してるPythonseleniumを使って自動化してみました
本記事ではその際に手こずった点をあげていこうと思います

Seleniumを使うには最低限のHTMLの知識を要するため、事前に簡単なHPを作成しておくのがよいと思いました。

2. 環境

OS : Windows 7 Professional
ブラウザ : InternetExplorer11
seleniumで操作するためにはドライバが必要。IEの場合IEDriverServerが必要となります。
Python : Python3.7

3. 詰まったところ

拡大率によるエラー

  • エラーコード
selenium.common.exceptions.SessionNotCreatedException: 
Message: Unexpected error launching Internet Explorer. Browser zoom level was set to 105%. 
It should be set to 100%
  • 原因
    拡大率が100%ではなかったこと

  • 解決法
    拡大率を100%にしてあげれば解決。 普段のブラウザ操作では頻繁に拡大表示するので高確率でエラーがでちゃいます。

プロキシの例外の設定によるエラー  

  • エラー
要求されたURLからデータを取り出せませんでした。
  • プロキシ(代理) とは?
    ネットサーフィンをする時、コンピュータはブラウザからサーバへ情報の提供を申請し、サーバはその申請に応じてブラウザへ情報を送信している
    プロキシはブラウザとサーバの間に存在する、中継役である
    プロキシが存在することでブラウザは身元を隠すことが出来るメリットがある
    インターネット接続を限定的に制限したい時などに設置される

  • 原因
    プロキシが設定されている場合localhost(127.0.0.)への接続にプロキシを経由することがある。
    プロキシに対してlocalhost(127.0.0.
    )でアクセスしようとするとプロキシ自体が通信を拒否してしまう。
    Python〜IEDriverServer間では通信にlocalhostや127.0.0.*が使われている。
    そのため、PythonからIEdriverServerにアクセスを行う際に、プロキシが接続を拒否してエラーになってしまう。

  • 解決法
    IE→インターネットオプション→接続→LANの設定→詳細設定→プロキシの例外、にlocalhostと127.0.0.*を追加する
    これにより、localhostへの接続時にプロキシを経由しなくなるため、エラーは発生しない

使用するドライバの指定(Ie)

使用するドライバのPATHを引数に指定し、IEのWebDriverのインスタンスを作成します。
webdriver.ブラウザ名(DriverのPATH)と書くのですが、IEの場合、Iは大文字、eは小文字 = Ieとします

Selenium python internet explorer - Stack Overflow

driver = webdriver.Ie(r"C:¥Users¥ユーザ名¥IEDriverServer.exe")

PATHの書き方

Windowsならではの問題ですが、windowsのPATHは¥マークで区切られます。
pythonでは¥マークを用いたエスケープシーケンスが存在するため、前述でドライバの在処を指定していますがPATHをコピペしてシングルクォートで囲むだけでは正確に在処を教えてあげることができません

www.pythonweb.jp

エスケープシーケンスを無効にするためにはraw文字列にしてあげればOKです

  • 方法
r'文字列'
r"文字列" #RでもOK

ウィンドウを切り換える

(下記サイトは今回Seleniumを使う上で何度も参考にさせていただきました。)

www.seleniumqref.com

Aシステムで業務を行っていると、新規ウィンドウが立ち上がります。普段IEで業務を行う上では、インターネットオプションでタグ ブラウズの設定ポップアップの発生時常に新しいタブでポップアップを開くにしているので新規タブで表示されるのですが、Seleniumで操作するブラウザでは関係ないらしく、新規ウィンドウで表示されてしまいます。新規ウィンドウが自動的にSeleniumの操作対象になるのかと思いきや、ウィンドウの切り換え作業を行わないと新規ウィンドウの操作ができないようです。

ウィンドウを識別する方法としてwindowhandleという情報を用い、それを元にウィンドウ切り換えを行います。手順としては以下のとおり

1. windowhandleを取得する
2. 取得したwindowhandleを使ってウィンドウを切り換える

  • コード
driver.window_handles #全ウィンドウのwindowhandleを取得
driver.switch_to_window(windowhandle) #引数に取得したwindowhandleを指定して切り換え
  • 実用例
handle = driver.window_handles #全windowhandleを取得
driver.switch_to_window(handle[1]) #インデックス番号でwindowhandleを指定して切り換え

ちなみにdriver.current_window_handleとすると現在操作権のあるウィンドウのwindowhandleだけを取得できます

flameの指定

HTMLにはfleamという1つのページに複数のページを埋め込むことが出来るタグが存在します。fleamによって分割されているサイトでSeleniumを使いたい時は、欲しい要素がどこのfleamに存在するかをHTMLから判断し、fleam切り換えをする必要があります。現在のfleamに存在しない要素を指定して情報を取得しようとしても、エラーになってしまいます

  • 切り換え方法
driver.switch_to_frame("frameName")

3. ナビゲート — Selenium Python Bindings 2 ドキュメント

ファイルのアップロード

通常ファイルをアップロードする時は参照をクリックするとエクスプローラが出現してファイルを選ぶ...といった流れになります。そのためSeleniumにもそのような機能が存在するのかと思っていました。

  • コード
driver.find_element_by_id('inputのidなど').send_keys(r"ファイルのPATH")

上記の例で言うと、HTMLから参照ボタンのinputを探し出してidなどで引数に指定、.send_keysメソッドの引数にアップロードしたいファイルのPATHを指定することでファイルのアップロードが可能です

https://www.dafuku.com/2014/12/selenium-file-upload.htmlwww.dafuku.com

待ち時間

毎朝のルーティンでは頻繁に画面を遷移します。遷移するたび読込時間が発生するので、コードの速さにブラウザが追いつかず、要素がありませんでしたと、エラーがでてしまう。そんなときに使えるのがSelenium待機です。

5. 待機 — Selenium Python Bindings 2 ドキュメント

softwaretest.jp

待機

Selenium待機には2種類あります

1. 暗黙的な待機 -Implicit Wait-

待機しないと利用できない要素を見つける時に、指定した時間内ポーリング(条件を満たしたら処理を行わせること)させることができます
つまり、指定した時間内で要素が見つかればすぐに次のコードを実行し、待っても要素が見つからなければエラーになる。
読み込んでいる間は要素の取得が不可能なので、その間待ってくれます。一度設定してあげればWebDriverが起動中有効だそうです。今回私はこれを使いました

  • コード
from selenium import webdriver

driver = webdriver.Ie(r"IEDriverのPATH")
driver.implicitly_wait(20) # seconds

私は最初の頃time.sleep()で個別に待機させてました。ブラウザの読込が終了してもtime.sleep()に指定した時間はしっかり待機するので、テストコードを試すのに時間がかかってうんざりしていましたが、暗黙的な待機であれば要素が見つかれば即座に次のコードが実行されます。

2. 明示的な待機 -Explicit Wait-

暗黙的な待機との違いは、個別の箇所に対してより詳細な条件で待機させることができる点です。今回は使わなかったので調べていません。
後日
動作が安定しなかったため、明示的な待機を使いました。主に visibility待機を多用しました。要素が表示されるまで待機する命令です。

締めは特にないです