PythonのseleniumでChrome自動操作する時に使うビジーウェイトは大抵これで解決!

python:seleniumで要素を取得するまで待機 プログラミング

数日前にPythonのseleniumでGoogle検索を行う方法について記事にしたばかりですが、またselenium関係の記事になります。

seleniumはGoogle ChromeやFirefoxなどのブラウザを自動操作することが可能なツールであり、Java, Ruby, JavaScript, Pythonなどの様々なプログラミング言語で使用する事ができます。

しかし、ブラウザをそのまま自動操作するという特性のため、何も考えずにただ自分が想像した通りにコードを記述しても、うまく動かなかったりします。

その原因の一つとして、目的の要素が表示される前にコードを実行してしまい、そのまま固まってしまうケースがよく挙げられます。

スポンサーリンク

目的の要素が表示される前にコードが実行されてしまうケースってどんな時?

目的の要素というのは、例えば、あるサイト上に表示されているリンク、ボタンなどを指すものとします。

プログラムコードで特定のリンクをクリックさせるよう記述した場合、コードが間違っていないのであれば問題なくプログラムが実行され、目的のリンクがクリックされてリンク先のページに飛ぶことができます。

しかし、プログラムコードに間違いがないにも関わらず、うまくいかないケースというのが多々あります。

そのケースとしてよくあるパターンが、ページが完全に読み込まれる前にコードを実行しようとしてしまう事が挙げられます。

例えば、あなたがネットサーフィンする時の事を思い出して欲しいのですが、好きなサイトにアクセスした時にロードが発生しますよね?

ロード中は画面が真っ白か、もしくは順次ロードされていって徐々にサイトが表示されていくと思います。

もしサイトがロードされずに画面が真っ白なままだったら、あなたはその先何もできなくなりますよね?

何も表示されてなかったら、どこに目的のリンクやボタンがあるのか分からないので、そのまま立ち往生するハメになります。

これはPCやスマホでネットサーフィンしたことがある人ならほぼ全員が直面したことのある問題だと思います。

実は、ブラウザを自動操作するためのプログラムコードも、全く同じような問題に直面します。

分かりやすくするために厳密な説明はしませんが、要するにページが完全に読み込まれていないせいで、プログラム側からしても目的のリンクやボタンが見つからず、そのままフリーズしてしまうということです。

まだ完全に画面が表示されていない段階でコードを実行してしまうので、その結果フリーズし、エラーとなってしまうのです。

スポンサーリンク

目的のリンクやボタンが表示されるまで待機するコードが必須

プログラムは人間が記述した通りにしか動きません。

なので、必要最低限のコードしか記述していなければ、本当にその通りにしか動きません。

人間で例えるならば、言われた事しかできない人というのがまさに当てはまります(僕のような人間です(笑))

ブラウザというのはページ遷移するたびに必ずロードが発生するものなので、遷移した後は目的のリンク、ボタンが表示されるまで待ってから次のリンクをクリック、もしくはタップする・・・というのが人間の行動です。

人間は目の前で起こっている物事から、これから先に行うべき行動を予見する力があるので、仮に表示が遅いサイトに遭遇したとしても『表示されるまで待ってみるか・・』と自然に考えたりするものです。

しかし、プログラムにはそんな器用な事は一切できません。

そのため、ページ遷移した後に目的のボタンをクリックするコードをそのまま書いて実行したりすると、ページの読み込みを一切考慮せずにそのまま実行してエラーが発生したりするのです。

この問題を回避するためには、『ページがロードされている間は待っていなきゃ駄目だよ!』という事をプログラムにもしっかり理解させる必要があります。

ちなみに、次の動作を実行できるようになるまでページの読み込みを待つ事を専門用語でビジーウェイト(busywait)って言ったりします。

スポンサーリンク

Pythonのseleniumでビジーウェイトを実装する方法

Pythonのseleniumでブラウザのページ読み込みを考慮した記述を行いたいのであれば、以下のモジュールをインポートしておけば大抵間違いないでしょう。

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

これらのモジュールの使い方は以下の通りです。

#モジュールのインポート
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

#Chromeの起動
driver = webdriver.Chrome()
#目的のページに遷移
driver.get("https://www.◯◯◯◯◯◯◯.com/")

#目的の要素が表示されるまで待機
WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.NAME, "name"))
    )

#目的の要素(リンクやボタンとか)をクリック
driver.find_element_by_name("name").click()

1行目の
from selenium import webdriver
の部分は、Chromeを自動操作するために必要なモジュールなので必ず記述します。

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

の3つが、seleniumでビジーウェイトを実装するために必要なモジュールになります。

まず、

driver = webdriver.Chrome()
driver.get("https://www.◯◯◯◯◯◯◯.com/")

の2行で目的のページに遷移します。

そして初心者がよくやる間違いとしては、いきなりこの後に、

driver.find_element_by_name("name").click()

と書いてしまう事です。

これだとページ遷移した直後に、まだ表示すらされていないリンクやボタンなどをクリックしようとしてしまう可能性が非常に高いです。

なので、この記述を書く前に次のコードを先に挟んでおきましょう。

WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.NAME, "name"))
    )

このコードの厳密な解説はしませんが、とりあえず

WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.属性名(必ず大文字), "属性の中身"))
    )

この形だけ覚えておいたら、目的の要素が表示されるまで待機するプログラムを書くのに大抵困らなくなるはずです(属性名に関しては必ず大文字で入力して下さい)。

ちなみにEC.visibility_of_element_located((By.属性名, “属性の中身”))の()が二重カッコになっているのは間違いではありません。”属性名と属性の中身をタプルで渡している”という意味ですが、詳しい事は各自で調べて下さい。

あと、このコードは次のように1行で書くこともできます。

WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.属性名, "属性の中身")))

しかし横に長くなるのでプログラムの可読性が悪くなり、あまりおすすめできません。

属性名と属性の中身の調べ方

Googleの検索窓を例に解説します。

Googleの検索窓の上で『右クリック』→『検証』とクリックすると、次のように検索窓のHTML要素が記述された部分が青くハイライトされます。

検索窓のhtml

青い部分を拡大してみます。(クリックで画像を拡大できます。)

HTML要素:属性と属性の中身

Google検索窓の要素に存在する属性として使えそうなものは、classとnameの2種類が挙げられます(これ以外の属性を指定する場合はxpathを使用するといった方法もありますが、かなり話が複雑になってくるので、今回はこの2つに絞ります)。

まずclass属性ですが、
class = “gLFyf gsfi”
と表記されています。

この時、class属性の中身は『gLFyf gsfi』と表記されているので、うっかりそのままコピーしそうになってしまいますが、ここで注意してください。

『gLFyf gsfi』をよく見ると、間に半角スペースがありますよね?

これは、class属性の中身として、『gLFyf』『gsfi』の2種類が存在しているという意味になります。

そのため、この属性を割り当てる際は次のような感じで記述します。

WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.CLASS, "gLFyf"))
    )

このように属性の中身が複数個存在している場合、そのうちのどれか1つを指定します。大抵は一番左のものを選んでおけば大丈夫だと思います。

次にname属性ですが、
name = “q”
と書かれています。

この場合は素直に次のように書けます。

WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.NAME, "q"))
    )

今回紹介した以外にも要素の取得方法は色々あるので、もっと詳しい情報が欲しい方は以下のサイト様を覗けば色々得られると思います。

・要素の取得方法がまとめられたページ

4. 要素を見つける — Selenium Python Bindings 2 ドキュメント

・Pythonのビジーウェイトの種類に関する説明(英語なので注意)

5. Waits — Selenium Python Bindings 2 documentation

とりあえず今回は以上になります。

最後まで読んでいただきありがとうございました。

コメント