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を使って自動化してみました
本記事ではその際に手こずった点をあげていこうと思います

ちなみに、自動化させた結果これまで感じていた多大なストレスから開放されました、Python様様です。
私は職業エンジニアではないのですが、Pythonを勉強したことで今の業務の効率化を図ることができました。
プログラミングを身につけることは自分の人生にメリットしかないな...とつくづく感じています。
職業にしたい
又、今回Seleniumを使いましたが以前簡単な使い方を以下のとおり学んでいます

atc.hateblo.jp

正直、上記の記事の時点ではSeleniumについてしっくり理解できていませんでした。なぜならSeleniumを使うには最低限のHTMLの知識を要するからです。しかし先週、職場のイントラの一部の制作を任されHTMLを学んだお陰でSeleniumで自由自在にスクレイピングできるようになりました。Seleniumを使いたいがHTMLを全く知らない場合はまずProgateで軽く触れてみたり、簡単な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%にしてあげれば解決します。拡大させてよ...字が小さいの...
    普段のブラウザ操作では頻繁に拡大表示するので高確率でエラーがでちゃいます。これってIEだけなんでしょうか

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

  • エラー
要求された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システムで業務を行っていると、2度新規ウィンドウが立ち上がります。普段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に存在しない要素を指定して情報を取得しようとしても、エラーになってしまいます(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を指定することでファイルのアップロードが可能です
すごくスマート、サイコー

www.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待機を多用しました。要素が表示されるまで待機する命令です。

締めは特にないです