面倒なブラウザ操作をSeleniumを使って自動化する その二(エラー回避&通知編)

お久ぶりです。中村です。

前回のselenium紹介の続き物です。

今回は下記2つについてご紹介します。
・なるべくエラーにならない形でのブラウザ操作の自動化
・もしエラーになった場合に通知を行う方法

なぜ、そんな気遣いが必要なのか

処理が止まると意図していたものとは違った結果が返ってきたりと、望まない挙動をしてしまいます。
自動でやりたかったことができていない。を避けるためになるべくエラーにならない気遣いが必要なのです。

本題に入る前に 例外処理について

pythonには実行中エラーが起こった場合に、別の処理を行うといった例外処理が可能です。
この例外処理をしていないと、エラーになった時点で処理が止まります。
実際に見てみましょう。

エラーで止まってしまう例

下記のスクリプトを実行してみましょう。
登録されていないドメインを見に行ったため、ページ接続の段階でエラーになりました。
そのため、ページ接続後の本来実行して欲しい処理が実行されていません。

#ライブラリ読み込み
from selenium import webdriver

#クロームの立ち上げ
driver=webdriver.Chrome()

#ページ接続 ★ここで処理が止まってしまう★
driver.get('https://存在しないページ.jp')

#ページ接続後に実行してほしい処理
print("実行してほしい処理")

#ブラウザの終了処理
driver.close()

例外処理で対策した例

次は下記のスクリプトを実行してみましょう。
止まってしまう例と同じく、登録されていないドメインを見に行ってしまいましたが、
エラーメッセージの出力+ページ接続後の本来実行して欲しい処理が実行されています。

#ライブラリ読み込み
from selenium import webdriver

#クロームの立ち上げ
driver=webdriver.Chrome()

try :#★エラーが起きそうな処理
    #ページ接続
    driver.get('https://存在しないページ.jp')
except :#★エラーが起きた場合に実行する処理
    #今回はメッセージのみ出力
    print("もしかしてページのURL間違えてない・・・?")

#ページ接続後に実行してほしい処理
print("実行してほしい処理")

#ブラウザの終了処理
driver.close()

★印がついている【try :】【except :】の2箇所が例外処理の肝になります。
簡単に説明します。

【try :】のブロックにエラーが起きそうな処理(今回はページ接続)を入れておきます。
もし、このブロック内でエラーが起きた場合に【 except : 】のブロック内の処理が実行されます。

【try :】のブロック でエラーが起きなかった場合は、【 except : 】 のブロックは実行されません。

※詳細は下記の公式ドキュメントを参照くださいmm
https://docs.python.org/ja/3/tutorial/errors.html

ということで本題

ページを読み込む際にアクセス集中などでページ読み込みが遅かったりすると、
決め打ちで待った秒数待機しても要素が見つけられないなどのエラーが起こったりする場合があります。

逆にページの読み込みが既に終わっているのに、決め打ちの待機時間が長く無駄に待ってしまう場合もあります。

これらを避けるために、要素が見つかるまで最大〇〇秒は待機するといったことを行います。

処理待ち時間を決め打ちにすることをやめる

[ 最大〇〇秒は待機する ] には暗黙的・明示的の2種類の待機方法があります。
今回は暗黙的な待機をメインにご紹介します。

明示的な待機

ある特定要素に対してのみ待機を行う方法です。
暗黙的な待機よりも細かい状態の判定ができます。

[ クリック可能になったら ]や[ 指定された要素が選択されているか ]などの
細かい判定をしたいときはこちらを使ったほうが便利です。

この内容だけで1記事かけちゃうので、次回以降でご紹介します。

暗黙的な待機

全ての要素に対して一括して同様の待機を行う方法です。
手軽に行うにはこちらがおすすめです。

要素が表示されるまで待つ

下記のようにwebdriverの立ち上げ後に【implicitly_wait(最大待機秒数)】をセットするだけです。
デフォルトは0(待機無効)になっています。

#ライブラリ読み込み
from selenium import webdriver

#クロームの立ち上げ
driver=webdriver.Chrome()

#最大待機時間を10秒にセット
driver.implicitly_wait(10)

実際に指定した秒数待機するか試してみる

下記のスクリプトを実行してみましょう。
出力に【検索にかかった秒数:5.014358699999999】のような、約5秒の数値が表示されたと思います。

確認できた場合は★印のコメントをしているところを0(待機無効)や任意の秒数に変えて
指定秒数の待機が出来ているか試してみましょう。

#ライブラリ読み込み
from selenium import webdriver
import time

#クロームの立ち上げ
driver=webdriver.Chrome()

#★最大待機時間を5秒にセット★
driver.implicitly_wait(5)

#ページ接続
driver.get('https://account.onamae.com/accountCreate')

#待ち時間計測開始
time_start = time.perf_counter()

#エラーが起きそうな処理
try :
    #存在しないIDを探させて、最大待機時間が有効になっているか確認する
    driver.find_element_by_xpath('//*[@id="存在しないID"]').click()

#エラーが起きた場合に実行する処理
except :
    print("もしかしてページのURL間違えてない・・・?")

#待ち時間計測終了
time_end = time.perf_counter()
print("検索にかかった秒数:"+str(time_end- time_start))

#ブラウザの終了処理
driver.close()

エラーになった時に通知する

上記でなるべくエラーにならない形の自動化を行いました。
ただ、こういったことをしてもエラーになるときはなります。
せめて、エラーになった時に通知を送ってもらいましょう。

通知方法はメールやらSlackやらSMSやら色々あります。
今回は皆さんが身近に試せる【LINE Notify】での通知方法をご紹介します。

LINE Notify API とは

IFTTTやGitHubなどと連携して通知を行う事ができるLINEの公式サービスです。
APIを利用することで自分のスクリプトから任意のテキストや画像の送信が可能です。

LINE Notify
https://notify-bot.line.me/ja/

Webサービスと連携すると、LINEが提供する公式アカウント”LINE Notify”から通知が届きます。複数のサービスと連携でき、グループでも通知を受信することが可能です。

通知までの流れ

LINE Notify側の準備

下記の流れでアクセストークンの取得を行います。
添付画像で一連の流れを確認いただけます。
アクセストークンは通知を送る際に必須となる鍵のようなものです。

【ログイン】
   ↓
【右上のアカウント名からマイページへ移動】
   ↓
【アクセストークンの発行(開発者向け)をクリック】
   ↓
【通知先のトークルーム選択】
  ↓
【アクセストークンをメモ】

アクセストークン取得までの流れ

line側の準備

通知するトークルームにLINE Notifyを追加します。
1:1でLINE Notifyから通知を受け取る場合は追加不要です。

通知のテスト

下記のスクリプトを実行して、通知が行えるか確認してみましょう。
※取得したアクセストークンを書き換えるのを忘れないように注意してください

#ライブラリ読み込み
import requests

#Lineへテキストの送信
url = "https://notify-api.line.me/api/notify"
access_token = '取得したアクセストークン'
headers = {'Authorization': 'Bearer ' + access_token}
message = "通知できてるかテスト"
payload = {'message': message}
r = requests.post(url, headers=headers, params=payload,)

上手く通知が出来ていると、LINEに下記のようなメッセージが届きます。

エラーになったときの通知をしてみる

では通知ができることを確認できたので、下記のスクリプトで実際にエラー通知をしてみましょう。
基本的に欲しい情報は、エラー内容/エラー時の画面のキャプチャです。
※取得したアクセストークンを書き換えるのを忘れないように注意してください

#ライブラリ読み込み
from selenium import webdriver
import requests


#Lineに通知する関数 通知文、通知画像を引数に渡す
def sendLine(text,imgPath):
    url = "https://notify-api.line.me/api/notify"
    access_token = '取得したアクセストークン'
    headers = {'Authorization': 'Bearer ' + access_token}
    payload = {'message': text}
    files = {'imageFile': open(imgPath, 'rb')}
    r = requests.post(url, headers=headers, params=payload, files=files,)


#クロームの立ち上げ
driver=webdriver.Chrome()

#最大待機時間を5秒にセット
driver.implicitly_wait(5)

#ページ接続
driver.get('https://account.onamae.com/accountCreate')

#エラーが起きそうな処理
try :
    #存在しないIDを探させて、最大待機時間が有効になっているか確認する
    driver.find_element_by_xpath('//*[@id="存在しないID"]').click()

#エラーが起きた場合に実行する処理 ※eはエラー内容が格納される
except Exception as e:
    #エラー時の画面キャプチャを取得
    driver.save_screenshot('キャプチャ.png')
    #エラーを通知
    sendLine("エラーが発生しました\n"+ str(e),"./キャプチャ.png")

#ブラウザの終了処理
driver.close()

上手く通知が出来ていると、LINEに下記のようなメッセージが届きます。
これにより、どういったエラーで止まってしまったのかがある程度わかるようになります。

色々な使い方ができます

今回はエラーがあった時に通知を行いました。
エラー通理だけではなく、一般的な人気商品が入荷されたときや、予定していた作業が無事完了したときなど
自分が役に立つように通知をすると更に便利になります。

次回もselenium関連の記事をかけたらと思っていますのでよろしくお願いしますmm

24時間動かすのはちょっと…

自分のPCを24時間使うのはちょっと…と思う方はConoHa VPSを使っていただければ幸いです!
ただいま9周年でイベント開催中です🎉

前回記事

ブログの著者欄

中村 槙吾

GMOインターネット株式会社 インフラ・運用グループ 運用保守チーム

2017年よりGMOグループにて運用保守業務に従事。 H/W保守や障害対応などの傍らで、運用効率化の取り組みを行う。

採用情報

関連記事

KEYWORD

採用情報

SNS FOLLOW