【Python】Seleniumの使い方【ウェブブラウザ操作の自動化】

PythonでのSeleniumの使い方の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Python での Selenium の使い方について解説していきます。特に、このページでは「ウェブブラウザの操作」という観点で、Selenium の使い方の説明を行なっていきます。

Selenium がどういったフレームワークであるのか?については下記ページで解説していますので、Selenium 自体について知りたい方や、Selenium のことをご存知ない方は、まずは下記ページを読んでみていただければと思います。

Seleniumについての解説ページアイキャッチ【Python】Seleniumとは?

Selenium を利用するための前準備

早速 Selenium の使い方について解説していきたいところですが、Selenium を利用する際には前準備が必要となりますので、その準備について解説しておきます。

Selenium 利用時のソフトウェアの全体構成

まず、Selenium を利用時のソフトウェアの全体構成について説明してておきます。これを理解しておけば、Selenium を利用する際に必要となるソフトウェアを自然と洗い出すことができます。その後、必要なソフトウェアのインストール方法の説明していきます。結論を先に言ってしまえば、Selenium を利用する際に必要な準備とは、基本的には Selenium 自体のインストールのみになるかと思います。

ソフトウェアの全体構成

Selenium 利用時のソフトウェアの全体構成は下図のようになります。

Seleniumでウェブブラウザの操作を行う際のソフトウェアの構成

まず、今回は Python での Selenium の使い方を解説するページですので、Python から Selenium を利用することになります。なので、当然 Python と Selenium が必要となります。

また、ここでいう Python は Python インタプリタを表しており、Python が Python スクリプトを実行する事になります。そして、この Python スクリプトから Selenium を import し、Selenium から提供されるクラスやメソッドを利用してウェブブラウザの操作を行うことになります。この Python スクリプトはプログラマー自身が作成する必要があり、Selenium を利用した Python スクリプトの作り方は Selenium の使い方 の章で解説していきます。

さらに、Selenium で実現するのはウェブブラウザの操作となりますので、操作対象となるウェブブラウザも必要です。また、実はウェブブラウザの制御は Selenium が直接行うのではなく、Selenium からの通信による指示を受け、ウェブブラウザのドライバが行う事になります。

したがって、このウェブブラウザのドライバもインストールしておく必要があります。このドライバはウェブブラウザ毎に用意されているため、操作対象のウェブブラウザに合わせてドライバをインストールしておく必要があります。例えば、操作対象のウェブブラウザが Chrome である場合、Chrome ドライバを事前にインストールしておく必要があります。また、この用意するドライバはウェブブラウザのバージョンに合ったものを用意しておく必要があります。

ウェブブラウザに合わせたドライバが必要であることを示す図

したがって、Selenium を利用する際には、ここまでに紹介してきた Python・Selenium・ウェブブラウザ・そのウェブブラウザのドライバを事前にインストールしておく必要があります。そして、ウェブブラウザの操作が適切に行われるよう Python スクリプトを実装する必要があります。

WebDriver

準備するソフトウェアとしては以上となるのですが、ついでにここで WebDriver について紹介しておきたいと思います。

この WebDriver は Selenium から提供されるクラスであり、この WebDriver のインスタンスがウェブブラウザのドライバと通信を行うことになります。したがって、ウェブブラウザの操作を行うためには、事前に WebDriver のインスタンスを生成しておく必要があります。そして、この WebDriver のインスタンスからメソッドを実行してウェブブラウザの操作を実現していくことになります。

WebDriverがウェブブラウザのドライバと通信を行なう様子を示す図

また、WebDriver には各種ウェブブラウザに対応するサブクラスが存在し、実際には WebDriver のサブクラスのインスタンスがウェブブラウザのドライバと通信を行うことになります。例えば Chrome に対応する WebDriver のサブクラスとして Chrome クラスが用意されており、Chrome クラスのインスタンスが、Python スクリプトからの指示に応じてブラウザのドライバと通信を行うことになります。

Chromeを操作するためにWebDriverにもChrome用のものを選択する必要があることを示す図

このように、WebDriver とブラウザのドライバとが通信を行うことで、Python スクリプトで記述された処理に応じてウェブブラウザの操作が実現されることになります。

ちょっとややこしいかもしれませんが、ここで重要なのは、Selenium からウェブブラウザの操作を行うためには操作対象のウェブブラウザに応じた WebDriver のサブクラスのインスタンスを生成する必要があるという点になります。

後述でも解説しますが、これさえ行えばウェブブラウザのドライバも自動的にインストール・起動されることになりますし、Python スクリプトからは WebDriver のサブクラスのインスタンスへのメソッドの実行により、WebDriver が必要に応じてウェブブラウザのドライバと通信してくれます。

そのため、ウェブブラウザのドライバの存在は実装時にはほとんど意識する必要はないです。WebDriver がウェブブラウザの操作・制御を行なってくれていると考えて実装して良いです。

実装時にウェブドライバの存在を意識しなくて良いことを示す図

ただ、Selenium を利用してウェブブラウザの操作を行なっても上手く動作しないような場合、もしかしたら原因はウェブブラウザのドライバにある可能性もあります。問題の原因を探る際にはウェブブラウザのドライバも意識する必要があるため、ウェブブラウザのドライバの存在については頭に入れておくと良いと思います。

スポンサーリンク

必要なモジュールのインストール

続いて、Selenium を利用する際に必要となるモジュール等のインストール手順を説明していきます。

Python のインストール

ただし、このページは Python 利用者の方に読んでいただくことを前提としており、既に Python をインストール済みの方が多いと思いますので、Python のインストール方法については説明を省略させていただきたいと思います。

基本的には、下記のサイトから自身の環境(OS)に対応する Python のインストーラーをダウンロードし、それを実行してインストールすれば良いだけになると思います。

https://www.python.org/downloads/

仮想環境を導入し、その環境に Python をインストールしたい場合は「Python 仮想環境」等でググれば情報が出てくると思います。

ウェブブラウザのインストール

続いて、Selenium から操作を行いたいウェブブラウザのインストールを行なっておいてください。このページでは、基本的に後述の解説はウェブブラウザとして Chrome を利用することを前提として説明していきます。そのため、Chrome をインストールしておいていただくと今後の解説が理解しやすくなると思います。

インストール手順は公式の Google のページで解説されていますので、そのリンクだけ紹介しておきます。

https://support.google.com/chrome/answer/95346?hl=ja&co=GENIE.Platform%3DDesktop

Selenium のインストール

続いて、本題となる Selenium をインストールします。

Python をインストールしておけば、Selenium は pip でインストールすることが可能です。pip を利用したインストールコマンドの例は下記のようになります。仮想環境を利用している場合は異なる可能性があるので注意してください。

% python -m pip install selenium

コマンド実行後はインストールに成功したかどうかを確認しておきましょう!先程と同様に pip からインストールに成功しているかどうかを確認可能です。

% python -m pip list

実行結果に、下記のような selenium の行が存在すればインストールに成功したと判断することができます。右側に表示されているのがバージョンで、インストールタイミングによっては下記よりも新しいバージョンである可能性があります。

selenium    4.9.1

ウェブブラウザのドライバのインストール

準備の最後にウェブブラウザのドライバのインストールを行なっていきましょう。

今回はウェブブラウザとして Chrome を利用することを想定しているため、Chrome ドライバをインストールする必要があります。

…と言いたいところですが、実はウェブブラウザのドライバのインストール手順は不要です。詳細は下記ページで解説していますが、バージョン 4.6 以降の Selenium を利用する場合、Python スクリプトから Selenium を利用する際に、自動的にインストール済みのウェブブラウザに対応したドライバがインストールされるようになっています。

Selenium Managerの解説ページアイキャッチ【Selenium】Selenium Managerの紹介【ドライバのインストール手順は不要!】

したがって、バージョン 4.6 以降の Selenium を利用する場合、ウェブブラウザのドライバのインストール手順は不要となります。私が記事を書いているタイミングでの Selenium の最新バージョンは 4.9.1 ですので、この記事を読んでくださっているタイミングでインストールを行なったのであればウェブブラウザのドライバのインストールは自動的に行われることになります。

古いバージョンの Selenium を利用している場合は、Selenium のバージョンアップをしておくことをオススメします。

ということで、おそらく、このページを読んでくださっている方は既に Python とウェブブラウザはインストール済みではないかと思いますので、必要となる準備は Selenium のインストール or バージョンアップのみとなります。

実は、Selenium を利用する上でのネックはウェブブラウザのドライバのインストール手順だったのですが、これが不要となったため、Selenium はかなり使いやすくなっています。

Selenium の使い方

Selenium を使う準備が整ったので、次は Selenium の実際の使い方、すなわち Selenium を利用する際の Python スクリプトの実装手順について解説していきます!

Selenium を利用する際の処理の流れ

最初に、Selenium を利用する際の処理の流れを簡単に説明しておきます。

Selenium を利用するための前準備 でも説明したように、Python スクリプトからは WebDriver のサブクラスのインスタンスを生成し、そのインスタンスからドライバを介してウェブブラウザの操作を行うことになります。そのため、Selenium でウェブブラウザを操作するためには、その操作対象となるウェブブラウザに対応する WebDriver のサブクラスのインスタンスを生成する必要があります。

WebDriver および WebDriver のサブクラスは webdriver モジュールで定義されていますので、webdriverimport が必須となります。その他の import が必要なモジュールに関しては必要になったタイミングで随時紹介していきます。

また、今回は操作対象のウェブブラウザを Chrome として解説していきたいと思います。この場合、WebDriver のサブクラスとしては Chrome クラスを利用することになり、Chrome クラスのインスタンスを生成することになります。

Chromeのインスタンスを生成する様子

WebDriver のサブクラスのインスタンス生成時には、ウェブブラウザのドライバとウェブブラウザ自体の起動も行われます。従って、Chrome クラスのインスタンスを生成すれば自動的に Chrome ドライバと Chrome が起動することになります。

そのため、あとは基本的に自身が行いたいウェブブラウザの操作に対応するメソッドを実行してやれば良いだけになります。

Chromeのインスタンス生成によりChromeドライバとChrome自体が起動する様子

ただ、ウェブブラウザの操作を行う目的はウェブブラウザ上に表示されたページに対して操作を行うことであることが多く、基本的には WebDriver のサブクラスのインスタンス生成後にページの表示を行う処理が必要となります。

ページを表示する様子

そして、ページの表示を行なった後は、ページ上の特定の要素に対して操作を行っていくことになります。例えば、ページ上の特定のボタン要素に対してクリックを行ったり、ページ上の特定の入力フィールド要素に対して文字列を入力したり、といった操作を行なっていくことになります。

ただ、こう言った特定の要素に対する操作を実施する際には、事前にその要素を取得しておく必要があります。したがって、ページ上の特定の要素に対する操作を実現する際には、まず操作対象の要素を探索して取得し、その取得した要素に対して操作を行うという2段階の手順が必要となります。

要素の取得と要素の操作を繰り返すことで、自身の行いたい操作をSeleniumから実現する様子

そして、この要素の取得および要素への操作を繰り返し実行することで、ウェブブラウザに対する様々な操作を自動的に行うことができるようになります。

ウェブブラウザに対する操作が全て完了すれば、最後にウェブブラウザを終了します。

ChromeドライバとChrome自体の終了を行う様子

まとめると、Selenium を利用してウェブブラウザの操作を行う際には、下記のような手順を実施する必要があります。

  1. WebDriver のサブクラスのインスタンスを生成する
    • ウェブブラウザのドライバとウェブブラウザが起動する
  2. ページを表示する
  3. ページ内の要素を取得する
  4. 取得した要素に対して操作を行う
    (3. と 4. は必要な分だけ繰り返す)
  5. ウェブブラウザを終了させる

これはあくまでも基本的な流れであって、この流れに必ずしも従う必要はありません。例えばウェブスクレイピングを行うのであれば、2. の「ページを表示する」を実行した後、そのページの HTML を取得して Beautiful Soup などによって HTML を解析するような流れになると思います。

このページでは、上記の基本的な流れに従ってウェブブラウザ(およびウェブブラウザに表示されるページの各種要素)に対する操作を行う手順を示していきますが、自身のやりたいことに応じて処理の流れは臨機応変に変更していただければと思います。

スポンサーリンク

WebDriver のサブクラスのインスタンスを生成する

続いて、先ほど示した各種手順についての詳細を解説していきます。

まずは WebDriver のサブクラスのインスタンスの生成について解説していきます。

Chrome 操作時のインスタンスの生成

前述でも少し触れましたが、WebDriver のサブクラスは webdriver というモジュールから定義されることになります。そのため、事前に webdriver import しておく必要があります。そして、この import 後に、WebDriver のサブクラスのコンストラクタを実行してインスタンスの生成を行います。

例えば Chrome の操作を行う際には下記のように webdriver.Chrome()、すなわち Chrome クラスのコンストラクタを実行してインスタンスの生成を行います。

インスタンス生成
from selenium import webdriver
driver = webdriver.Chrome()

以降で説明するウェブブラウザの操作は、基本的には WebDriver のサブクラスのインスタンス or 取得した要素のインスタンスからメソッドを実行することで実現していくことになります。

ここからは、WebDriver のサブクラスのインスタンスのことを単に driver と呼ばせていただきます。

ドライバとブラウザの起動

前述でも触れましたが、WebDriver のサブクラスのコンストラクタを実行した際には、そのサブクラスに対応するウェブブラウザのドライバとウェブブラウザ自体が起動することになります。例えば webdriver.Chrome() を実行した際には、Chrome ドライバと Chrome 自体の起動も行われることになります。

また、ウェブブラウザに対応するドライバが存在しない場合、Selenium Manager と呼ばれるツールが実行され、そのウェブブラウザに対応するドライバが自動的にインストールされ、インストール後に、そのドライバが起動されることになります。

Chromeのインスタンス生成によりChromeドライバとChrome自体が起動する様子

そのため、WebDriver のサブクラスのコンストラクタを実行さえすれば、Selenium からウェブブラウザの操作を行うにあたって必要になるソフトウェアが全て起動することになります。ですので、WebDriver のサブクラスのコンストラクタを実行して driver を生成すれば、後は driver からメソッドを実行してWebブラウザの操作を行なっていけば良いことになります。

MEMO

古いバージョンの Selenium では、WebDriver のサブクラスに executable_path 引数を指定してプログラマーが明示的にドライバのパスを指定する必要がありました

ですが、最近の Selenium では自動的にドライバのパスが設定されるようになっているため(さらには、存在しない場合は自動的にドライバのインストールも行われる)、executable_path 引数の指定は不要となっています

他のウェブブラウザの操作

先ほどは Chrome の操作を行う例で解説を行なってきましたが、Selenium からは Chrome 以外のウェブブラウザの操作を行うことも可能です。他のウェブブラウザを操作したい場合は下記部分を変更する必要があります。

他のウェブブラウザの操作
driver = webdriver.Chrome()

前述でも説明したように、ChromeWebDriver のサブクラスの1つで、同様のサブクラスがウェブブラウザごとに用意されています。なので、Chrome 以外のウェブブラウザを操作したいのであれば、Chrome ではなく他のクラスのインスタンスを生成する必要があります。例えば、Safari を操作したいのであれば Safari、Edge を操作したいのであれば Edge クラスのインスタンスを生成することになります。

例えば下記は、Edge を操作するために Edge のインスタンスを生成する例になります。もし Edge ドライバがインストールされていなければ、Edge() 実行時にドライバのインストールも自動的に行われます。この点も Chrome の時と同様になります。

Edgeの操作
# 2. WebDriver のインスタンス生成
driver = webdriver.Edge()

私も詳細な確認をしたわけではないのですが、手作業時に行うブラウザでの操作は各ブラウザで基本的に同じであるため、使用する WebDriver のサブクラスを変更したとしても、それ以降に行う処理はウェブブラウザの種類に依存しないはずです。例えば Selenium でのウェブアプリの操作例 で Selenium を利用したウェブブラウザ操作の例を紹介しますが、そこで示すソースコードは上記のように使用する WebDriverEdge に変更しても上手く動作することを確認しています。

また、ウェブブラウザによっては、Selenium から利用する際にそのウェブブラウザ独自のお作法が必要になることもあるので注意してください。例えば Safari の場合は、ウェブブラウザから操作を行うために事前に「開発」メニューから「リモートオートメーションを許可」にチェックを入れておく必要があります。

Safariブラウザの操作を行う際の注意点1

また、Safari のメニューに「開発」メニューが表示されていない場合は、事前に Safari の環境設定の「詳細」タブで メニューバーに"開発"メニューを表示 にチェックを入れておく必要があります。

Safariブラウザを利用する際の手順2

こんな感じで、他のウェブブラウザも操作可能ではあるものの、ウェブブラウザによっては Selenium から操作するために独自の作法が必要になることもあるので注意してください。Chrome の操作に関する情報はネット上にも多く存在するので、こだわりがなければ Chrome でのウェブブラウザの操作をまず試してみるのが良いと思います。

オプションの指定

また、WebDriver のインスタンスを生成する際にはオプションを指定することが可能です。

オプションを指定する際には、まず Options クラスのインスタンスを生成し、そのインスタンスから add_argument メソッドを実行させてオプション指定の追加を行い、その後 WebDriver のコンストラクタ(WebDriver のサブクラスのコンストラクタ)の options 引数にインスタンスを指定することになります。

実際のソースコードとしては下記のようなものになります。

オプション指定
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)

ポイントは、Options クラスはウェブブラウザ毎に用意されている点になります。そのため、そのウェブブラウザに対応する Options クラスのインスタンスを生成する必要があります。Chrome の場合は、上記のように selenium.webdriver.chrome.options で定義されている Options クラスを利用する必要があります。

また、上記ではオプションで --headless を指定しており、これによりウェブブラウザが起動しても画面に表示されないようになります。これは結構利用機会の多いオプションになると思いますので是非覚えておいてください。

ページを表示する

driver を生成した後は、基本的にはページの表示を行うことになります。

ページの表示は、driver から get メソッドを実行させることで実現することができます。引数には表示したいページの URL を指定します。

ページの表示
driver.get('開きたいページのURL')

get メソッドを実行すればウェブブラウザにページ表示の指示が出され、ウェブブラウザから引数で指定した URL のページを表示するためのリクエストがウェブサーバー等に送信されます。そして、そのリクエストの送信に対するレスポンスとして HTML・CSS・JavaScript 等を受け取り、それがページとしてウェブブラウザに描画されて表示されることになります。

これは、手動でウェブブラウザの URL バーに URL を指定する時と同じ動作であり、人ではなく Selenium が URL の指定を行うようになるだけです。

手動でのページの流れ

例えば、get メソッドの引数に https://google.com を指定すれば、お馴染みの Google のトップページを表示することができます。

そして、ページの表示によって、ページ内にボタンや入力フィールド等の要素が表示されますので、これらの要素に対して操作を行なっていくことになります。

ページ内の要素を取得する

では、続いてページ内の要素の操作方法について解説していきます。

要素を操作するとは?

ページ内の要素の操作に関しては、下記の2つの操作を組み合わせることで実現することになります。要素とは、ページ内を構成する部品のようなものであり、例としてボタン・入力フィールド・リンク・段落・見出しなどが挙げられます。

  • ページ内の要素を探索する
  • 見つけた要素に対して操作を行う

皆さんもマウス等を利用してウェブブラウザからページ内のボタンをクリックしたことがあると思います。その際には、大きく分けて下記の2つのようなことを行なっているのではないかと思います。

  • 目でクリックしたいボタンの位置を探索する
  • 見つけた位置に対してクリックを行う

基本的にはページ内には複数の要素が存在します。そのため、まず、その中からクリックしたい要素を見つけて特定することが必要となります。そして見つけた後に、クリックなどの操作を行うことになります。

人間がページ内の特定の要素に対して操作を行う様子

このように、ページ内の要素に対して操作を行う際には、人間が手作業で操作を行うにしても、操作対象の要素を見つけ、その見つけた要素に対して操作を行うという2つの動作が必要となります。そして、これは Selenium で操作を行う際にも同様のことが言えます。つまり、Selenium で要素に対して操作を行う際にも、まずその要素を探索して見つけ出し、その見つけた要素に対して操作を行う必要があります。

Seleniumがページ内の特定の要素に対して操作を行う様子

そして、ここではまず、前者の要素の探索方法について解説していきます。

要素の探索方法

では、Selenium で操作対象の要素はどうやって探索すれば良いのでしょうか?

人間が手作業で操作を行う際には基本的には目で探索することが多いと思いますが、Selenium の場合は HTML の構造を利用して要素の探索を行います。

ウェブブラウザで表示されるページは HTML から構成されています。そして、ページ内の各要素は HTML 内にタグとして記述されています。Selenium で要素の操作を行う場合、まずは、この HTML 内のタグを頼りに探索することになります。

例えば、タグには id 属性が指定されていることがあります。この id 属性は、タグに付加されたユニークな識別子であり、この id 属性を利用してタグを探索することができます。

id属性からタグを特定する様子

find_element メソッド

さらに、この要素の探索を行う際には、WebDriver クラスの提供する find_element メソッドを利用します。WebDriver が提供するメソッドですので、Chrome 等の WebDriver のサブクラスのインスタンスからも find_element は実行可能です。

find_element は下記の形式で引数を指定して実行することが多いです。find_element を実行する driverWebDriver のサブクラスのインスタンスである必要があります。

find_elementの実行
elem = driver.find_element(by, value)

引数の byvalue要素の探索条件を指定する引数となります。つまり、find_elementbyvalue とで指定する探索条件に一致する要素を見つけ出し、その要素を返却値として返却するメソッドとなります。返却される要素の数は1つのみとなります。

より具体的には、by には探索対象とする項目名を、value探索対象とする項目の値を指定します。例えば、下記のように find_element の引数を指定した場合、探索対象とする項目名が ID で、さらに、その IDsend_button である要素の探索が行われることになります。

find_elementの実行例
from selenium.webdriver.common.by import By

button = driver.find_element(by=By.ID, value='send_button')

これはすなわち、タグに設定されている id 属性が send_button である要素を探索する命令となります。そして、この探索条件に一致する要素が見つかった場合に返却値として目当ての要素を取得することができます。見つからなかった場合は例外が発生します。

find_elementの動作の説明図

例えば、下の図のような4つのボタンが存在するページにおいて、Send ボタンをクリックしたいとしましょう。このページには複数のボタンがあるので、この中からまず Send ボタンを探索して取得する必要があります。

ボタンの探索例

さらに、このページが下記のような HTML によって描画されているとします。この HTML の中で input タグがボタン要素に対応しており、4つ input タグあるのでページには4つのボタンが表示されることになります。Selenium で Send ボタンのクリックを実現したい場合、まずは下記の HTML の中から Send ボタンを探索して取得する必要があります。

ボタンが4つあるページのHTML
<html>
    <head>
        <title>Buttons</title>
    </head>
    <body>
        <input id="ok_button" name="ok" value="OK" type="submit">
        <input id="cancel_button" name="cancel" value="Cancel" type="submit">
        <input id="send_button" name="send"  value="Send" type="submit">
        <input id="skip_button" name="skip" value="Skip" type="submit">
    </body>
</html>

そして、その Send ボタンの取得を実現するのが、前述でも示した下記の処理となります。Selenium を利用してページを表示した後に下記のように find_element を実行すれば、id 属性が send_button である要素の探索が行われます。その結果、上記の HTML には id 属性が send_button である input 要素が存在するため、この input 要素を取得することができることになります。 そして、この input 要素はページの描画結果において左から3つ目の要素、すなわち Send ボタンとなります。

find_elementの実行例
from selenium.webdriver.common.by import By

button = driver.find_element(by=By.ID, value='send_button')

上記の find_element メソッドの実行によって、Send ボタン要素が button として取得することができることになるため、後はこの button に対して操作、例えばクリック操作を行えば、「Send ボタンのクリック」という操作を実現することができることになります。

このように、find_element を利用することで要素をページ内から探索して取得し、その要素に対して操作を実行するのが、ページ内の要素への操作を行う一連の流れとなります。

また、ここでは id 属性での探索例を示しましたが、by 引数に指定する値を変更すれば、他の項目で探索を行うこともできます。by 引数に指定可能な値に関しては、後述の by 引数に指定可能な値 で紹介します。

find_elements メソッド

find_element が1つの要素のみを返却するのに対し、find_elements は見つかった全ての要素を返却するメソッドとなります。つまり、find_elements メソッド利用すれば、複数の要素を一度に取得することができます。

引数に関しては find_element と同様で、find_elements メソッドの引数には byvalue の2つで探索条件を指定して実行することになります。ただし、find_element の返却値が1つの要素のみであるのに対し、find_elements メソッドの返却値は見つかった全ての要素が格納されたリストとなります。探索条件に一致する要素が見つからなかった場合は空のリストが返却されることになります。

例えば、先ほど示した下記の HTML によって表示されるページの場合、

ボタンが4つあるページのHTML
<html>
    <head>
        <title>Buttons</title>
    </head>
    <body>
        <input id="ok_button" name="ok" value="OK" type="submit">
        <input id="cancel_button" name="cancel" value="Cancel" type="submit">
        <input id="send_button" name="send"  value="Send" type="submit">
        <input id="skip_button" name="skip" value="Skip" type="submit">
    </body>
</html>

下記のように find_elements を実行すれば、4つのボタン要素を持つリストが返却されることになります。そして、このリストに対して for 文でループを回せば、リストの持つ全ての要素に対して操作を行うようなことも可能となります。

find_elementsの実行例
from selenium.webdriver.common.by import By

buttons = driver.find_elements(by=By.TAG_NAME, value='input')
for button in buttons:
    button.click()

by 引数に指定可能な値

先ほどの find_element の使用例では by 引数に By.ID を、find_elements の使用例では by 引数に By.TAG_NAME を指定する例を示しました。

この2つの例からも察していただけるように、by 引数には他にも様々な値を指定することが可能です。具体的な by に指定可能な値は下記となります。これらは全て、By クラスから提供されるクラス変数として定義されていますので、事前に By クラスの import をしておく必要があります。

  • By.ID:タグの id 属性で探索
  • By.XPATH:XPath で探索
  • By.LINK_TEXT:タグのリンクテキストで探索(全一致)
  • By.PARTIAL_LINK_TEXT:タグのリンクテキストで探索(部分一致)
  • By.NAME:タグの name 属性で探索
  • By.TAG_NAME:タグ名で探索
  • By.CLASS_NAME:タグの class 属性で探索
  • By.CSS_SELECTOR:CSS セレクタで探索

一番分かりやすいのが By.ID や By.NAME あたりになると思います。これらは HTML のタグの属性として指定されている id 属性 と name 属性を探索対象とするための指定となります。なので、特定の要素に対して操作を行いたい場合、まずは HTML を調べ、特定の要素に対応するタグの属性を調べてやれば value に指定すべき値を特定することができます。

ただし、id 属性 と name 属性は必須の属性では無いため、こららの属性が指定されていないタグも存在します。こういった場合は、どうやって特定の要素を探索すれば良いのでしょうか?

個人的にオススメなのは By.XPATH での探索になります。XPath って何?って思われる方も多いと思いますが、簡単に言ってしまえば、ページ内の要素の位置を特定するためのパスになります。これを聞いてもピンと来ない方も多いと思います。正直 XPath を完全に理解するのは難しいです…。

ですが、Selenium でページ内の要素を操作するだけであれば、XPath について深く理解しておく必要はないです。Selenium でページ内の要素に対して操作を行うために理解しておくべきことは、この XPath が要素を特定するための情報あること、かつ、この XPath は Chrome 等の検証機能を利用すれば簡単に調べることが可能であることの2つになります。

要は、Chrome さえあれば、ページ内の要素に対応する XPath を簡単に調べることができ、あとは find_elementfind_elements を実行する際に by 引数に By.XPATH を、value 引数に調べた XPath を指定してやれば、操作対象とする要素を簡単に取得することができます。

個人的にオススメなのは By.XPATH になりますが、同様に Chrome で CSS セレクタも簡単に調べることができるため、By.CSS_SELECTOR を利用するのでも良いと思います。いずれにしても、Chrome を利用することで Selenium での要素の探索が簡単に行うことができるという点がポイントとなります。

検証機能のでの XPath の取得

ということで、Selenium を利用する上では検証機能の利用方法を知っておいた方が良いです。

そのため、ここで簡単に Chrome での検証機能の利用方法について解説しておきます。この検証機能は非常に便利&無料で誰でも利用できる機能ですので、是非この機会に検証機能の使い方を覚えておきましょう!

検証機能を利用するためには、まず Chrome を起動して何らかのページを開きます。

そして、ページの中から操作対象としたい要素にマウスカーソルを合わせ、右クリックを行います。そして、右クリックメニューの中から 検証 を選択します。

検証機能の利用方法

すると、おそらくページの右側に検証タブが表示され、先ほど右クリックした要素に対応するタグがハイライトされた状態で表示されているはずです。

右クリックした要素がハイライトされている様子

次は、そのハイライトされているタグを右クリックし、右クリックメニューから コピー を選択、さらに、それによって表示されるコピーメニューの中から  XPathを コピー を選択します。

XPathのコピーの手順

これにより、右クリックした要素の XPath がクリップボードにコピーされたことになります。

あとは、find_elementfind_elements メソッドの value 引数にペーストしてやれば、探索対象とする要素の XPath の指定は完了となります。この手順の場合、value には XPath を指定することになるため by 引数に By.XPATH を指定することを忘れないようにしてください。

find_elementメソッドへのXPathの指定の仕方の説明図

具体的に XPath を指定した find_element メソッドの実行例は下記のようなものになります。value に指定している値が XPath で、XPath や HTML に詳しくない方にとっては意味不明かもしれないですが、上記の手順で XPath を指定してやれば、右クリックした要素をうまく取得することができるはずです。

XPathの指定例
elem = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')

このように、XPath は Chrome の検証機能を利用することで簡単に取得することができます。そして、この XPath の取得さえしてやれば、find_elementfind_elements メソッドの実行によって操作対象となる要素の取得を簡単に実現することができます。

もちろん、ここで実現すべきことは操作対象となる要素を取得することですので、別に XPath に拘る必要はないです。ですが、検証機能によって XPath が簡単に取得できることは覚えておくと良いと思います。ただし、XPath は万能というわけではなく、状況によっては XPath を指定すると目的の要素とは異なる要素が取得できてしまうようなケースもあります。この点に関しては、Selenium でのウェブアプリの操作例 で実例を示しながら説明していきたいと思います。

また、取得したい要素のタグも調べやすいので、XPath を調べるときだけでなく id 属性や name 属性そ調べる際にも検証機能は活躍すると思います。

ワイルドカード

また、value 引数にはワイルドカード * (アスタリスク) が利用可能であることも覚えておくと良いと思います。例えば前述で示した下記においては value 引数にワイルドカード * が利用されており、この * 部分は任意の文字列として扱われることになります。つまり、* 部分に関してはどんな文字列であっても良いことになります。

XPathの指定例
elem = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')

したがって、ページを表示した後に下記を実行することでページ内の全要素を取得するようなこともできます。下記では by=By.TAG_NAMEvalue='*' を指定しているため、タグ名に関わらず要素の取得が行われるため、結果的に全要素を取得することができることになります。

全要素の取得
elems = driver.find_elements(by=By.TAG_NAME, value='*')

今回はウェブブラウザの操作に焦点を当てており、この場合は、どちらかというと多くの要素を取得することよりも操作対象となる特定の要素に限定して取得することが重要となります。そのため、ワイルドカードを利用する機会は少ないかもしれませんが、ワイルドカードが利用可能であることは覚えておくと良いと思います。

スポンサーリンク

取得した要素を操作する

find_elementfind_elements で要素を取得したあとは、その要素に対して操作を行うことになります。

この操作は、取得した要素のインスタンスからメソッドを実行することで実現することができます。続いて、要素のインスタンスから実行可能なメソッドのいくつかを紹介していきます。

click

一番分かりやすいのが要素に対するクリック操作になると思います。このクリック操作は、click メソッドの実行により実現することができます。

ボタンをクリックする様子

例えば、フォーム内のボタン要素をクリックすることでデータの送信を行うようなこともできますし、チェックボックス要素に対してクリックを行うことでチェックの ON / OFF を切り替えるようなこともできます。

ボタンのクリック
button = driver.find_element(by=By.XPATH, value='ボタンのXPath')
button.click()

send_key

また、入力フィールド等の要素のインスタンスに send_key メソッドを実行することで文字列を入力することができます。

send_keyの説明図

send_key メソッドの引数には入力したい文字列を指定します。

文字列の入力
input_field = driver.find_element(by=By.XPATH, value='入力フィールドのXPath')
input_field.send_key('YamadaHanako')

clear

また、逆に入力フィールド等の要素のインスタンスに clear メソッドを実行することで文字列をクリアすることもできます。

clearの説明図

clear メソッドの引数への引数指定は不要です。

文字列のクリア
input_field = driver.find_element(by=By.XPATH, value='入力フィールドのXPath')
input_field.clear()

is_selected

また、is_selected メソッドで要素が選択中であるかどうかを調べることもできます。

is_selectedの説明図

例えば、is_selected メソッドによりチェックボックスのチェックが ON / OFF のどちらであるかを調べるようなことができます。

チェックボックス要素のインスタンスに is_selected メソッドを実行させれば、チェックが ON の時には True が、チェックが OFF の時には False が返却されることになります。したがって、is_selected メソッドの返却値が False の場合のみ click メソッドを実行させるようにすれば、そのチェックボックスのチェックを確実に ON にすることができます。

チェックボックスのON
check_box = driver.find_element(by=By.XPATH, value='チェックボックのXPath')
if not check_box.is_selected():
    check_box.click()

find_elementfind_elements

これは要素に対する操作とは少し違うものになりますが、要素のインスタンスからは find_elementfind_elements を実行することもできます。

HTML では、要素の中に他の要素が存在するような構成のものが存在します。例えば下記では、div タグの要素の中にボタン要素が存在しています。

要素の中に要素が存在する例
<html>
    <head>
        <title>Buttons</title>
    </head>
    <body>
        <div id="section1">
             <input id="ok_button" name="ok" value="OK" type="submit">
            <input id="cancel_button" name="cancel" value="Cancel" type="submit">
        </div>
        <div id="section2">
            <input id="send_button" name="send"  value="Send" type="submit">
            <input id="skip_button" name="skip" value="Skip" type="submit">
        </div>
    </body>
</html>

上記のような HTML のページにおいて、id 属性が section2div タグの要素の中にあるボタンを全てクリックするような操作は下記のような処理によって実現することができます。

要素の中から要素の探索
div = driver.find_element(by=By.ID, value='section2')
buttons = div.find_elements(by=By.TAG_NAME, value='input')
for button in buttons:
    button.click()

Selenium を使いこなすポイントの1つは要素の取得になります。要素から find_element 等を実行させることで、上記のように段階的に要素を限定しながら最終的な操作対象の要素を探していくことができる点も覚えておくと良いと思います。

データ属性

また、これはメソッドではありませんが、各要素のインスタンスから利用可能なデータ属性の一部、およびそれらの意味合いについてもいくつか紹介しておきます。

  • location:要素の位置
  • size:要素のサイズ
  • rect:要素のサイズと位置
  • tag_name:要素のタグ名
  • text:要素の本文
  • screenshot_as_png:要素のスクリーンショット

よく利用するのは text になると思います。例えば段落要素は <p>段落の本文</p> のような形式で記述されます。この 段落の本文 部分を取得したければ、まずはこの p タグの要素を取得し、その要素の text データ属性を参照すれば良いことになります。

また、使い所は多くないかもしれないですが、screenshot_as_png から要素のスクリーンショット画像が取得できる点も面白い点になると思います。

screenshot_as_png を利用することで、例えば下記のようにページ内の全要素のスクリーンショット画像を保存するようなことも可能になります。要素によってはサイズ(幅と高さ)が 0 のものもあるので、それらの要素を除いてスクリーンショットを保存するようになっています。

要素が大量に存在するページを表示してから下記を実行すると大量のスクリーンショット画像が PC に保存されることになるので注意してください。

全要素のスクリーンショット保存
elems = driver.find_elements(by=By.TAG_NAME, value='*')
for i, elem in enumerate(elems):
    if elem.size['height'] != 0 and elem.size['width'] != 0:
        with open('{}_{}.png'.format(elem.tag_name, i), 'wb') as f:
            f.write(elem.screenshot_as_png)

また、上記の例を見ていただくことで、要素のデータ属性よりサイズやタグ名を取得することができることも確認していただけると思います。

ウェブブラウザを終了させる

ページ内の要素に対する操作が完了すれば、後はウェブブラウザを終了させて作業完了となります。このウェブブラウザの終了は driver から quit メソッドを実行することで実現できます。

quit メソッドを実行した場合、ウェブブラウザが終了し、ウェブブラウザのドライバも終了することになります。

ウェブブラウザの終了
driver.quit()

ウェブブラウザ自体を操作する

ここまでが、ウェブブラウザの操作を行う際の Selenium での基本的な処理の流れとなります。

基本的に、ここまでは特にウェブブラウザ上に表示されたページの要素を操作することに焦点を当てて解説を行なってきましたが、続いてウェブブラウザ自体に対する操作の実現方法を説明しておきたい思います。

これらも基本的には、WebDriver に用意されたメソッドを利用して実現していくことになりますので、操作対象のウェブブラウザに対応する WebDriver のサブクラスのインスタンスを生成した後にメソッドを実行してウェブブラウザ自体の操作を行なっていくことになります。以降に紹介するメソッドの実行例においては、このインスタンスが driver であることを前提としたものになっています。

スクリーンショットの撮影

最初に紹介するのがスクリーンショットの撮影になります。

このスクリーンショットの撮影は save_screenshot メソッドの実行によって行うことができます。save_screenshot メソッドを実行することで、実行したタイミングでウェブブラウザに表示されているページのスクリーンショットが撮影され、引数で指定したパスに PNG 画像ファイルとして保存することができます。

スクリーンショットの撮影例
driver.save_screenshot('screenshot.png')

前述の データ属性 で要素単体のスクリーンショットをデータ属性から取得可能であることを説明しましたが、save_screenshot では要素単体ではなくウェブブラウザに表示されている部分全体のスクリーンショットを撮影することが可能となります。

もしかしたらバージョン等によって異なるかもしれませんが、どうもソースコードを見た感じだと、保存する画像のフォーマットは PNG のみとなっているようでした(引数に拡張子が .jpg などの .png 以外を指定しても、結局 PNG 画像として保存される)。

また、撮影したスクリーンショットをファイルに保存するのではなく、画像データとして取得したい場合は get_screenshot_as_png メソッドを利用してやれば良いです。

例えば下記のように処理を行えば、撮影したスクリーンショットを PIL の画像オブジェクトとして扱うことができます。

画像オブジェクトへの変換
png_data = driver.get_screenshot_as_png()
img = Image.open(io.BytesIO(png_data))

戻る・進む・更新

また、ウェブブラウザに用意されている「戻る」「進む」「更新」操作を Selenium から行うこともできます。これらはそれぞれ backforwardrefresh メソッドにより実現できます。

戻る・進む・更新
driver.back()
driver.forward()
driver.refresh()

タブの新規追加とタブの終了

また、ウェブブラウザのウィンドウにタブを新規追加し、そのタブ上にページを表示するようなこともできます。タブの新規追加は、driverswitch_to データ属性から new_window メソッドを実行することで実現できます(引数には 'tab' を指定します)。

また、driver から操作を行なっているタブやウィンドウのみを閉じる際には close メソッドを実行してやれば良いです(前述で紹介した quit メソッドの場合は、ウェブブラウザ自体が終了することになります)。

例えば下記を実行すれば、ウェブブラウザのウィンドウにタブが新規追加され、そのタブで Google のトップページが表示されることになります。そして、最後の close メソッドにより、そのタブが閉じられることになります。

タグの新規追加の例
driver.switch_to.new_window('tab')
driver.get('https://google.com')
driver.close()

ここでポイントになるのが、driver.switch_to.new_window を実行すると、driver からの操作対象が新規追加したタブに移るという点になります。なので、次の行で get メソッドを実行することで、新規追加したタブに Google のページが表示されることになります。

この操作対象が移るという点は、タブを閉じた時に注意が必要となります。タブを閉じたとしても driver からの操作対象は閉じたタブのままになります。

Seleniumが閉じたタブに操作しようとする様子

なので、その後 driver から何かしらの操作を行おうとしても、閉じたタブに対して操作を行うことになってしまって例外が発生することになります。これを防ぐためには、操作対象を元々開いていたタブに戻す等の処理が必要となります。この処理を行うのが driver.switch_to.window メソッドとなります。

driver.switch_to.windowメソッドで操作対象のタブを変更する様子

先ほど示したソースコードに対し、「操作対象を元々開いていたタブに戻す処理」を追加したものが下記となります。

操作対象を元々のタブに戻す
original = driver.current_window_handle
driver.switch_to.new_window('tab')
driver.get('https://google.com')
driver.close()
driver.switch_to.window(original)

上記において、操作対象を元々のタブに戻す処理を行なっているのが最後の行となります。この最後の行で実行している driver.switch_to.window は操作対象を他のタブ・ウィンドウに移すメソッドになります。引数には、操作対象の移動先となるタブやウィンドウのハンドルを指定する必要があります。ハンドルとは識別子みたいなものです。

で、ここで行いたいのは新たな操作対象を「元々操作していたタブ」に戻すことですので、1行目で保存しておいたハンドルを window メソッドの引数に指定するようにしています。driver.current_window_handle は、今現在 driver から操作しているウィンドウ(タブ)のハンドルを管理するデータ属性となります。

したがって、上記のような流れで処理を実行すれば、新たにタブを追加し、そのタブを閉じたとしても、以降の操作を元々操作していたタブに対して実行することができるようになります。

また、ここではタブに焦点を当てて解説を行いましたが、driver.switch_to.new_window('tab') ではなく driver.switch_to.new_window('windows') に変更すれば、新規ウィンドウを作成し、そのウィンドウに対して操作を行うことができるようになります。閉じ方や閉じた後の操作対象の変更方法はタブの時と同様になります。

ウィンドウサイズ関連の設定

また、ウェブブラウザのウィンドウサイズやウィンドウの位置を変更するようなことも可能です。

ウィンドウサイズ関連の設定を行うメソッドには下記のようなものが存在します。

  • maximize_window:ウィンドウのサイズを最大化する
  • minimize_window:ウィンドウのサイズを最小化する
  • set_window_size:ウィンドウのサイズを第1引数で指定した幅・第2引数で指定した高さに設定する
  • set_window_position:ウィンドウの左上の位置を第1引数で指定した x 座標・第2引数で指定した y 座標に移動する

サイズや座標はピクセル単位で指定する必要があります。また、座標の原点はパソコンの画面の左上端となります。

下記は、これらのメソッドの実行例となります。

ウィンドウサイズの設定例
driver.maximize_window()
driver.minimize_window()
driver.set_window_size(100, 200)
driver.set_window_position(500, 300)

スクリプトの実行

また、Selenium では表示しているページに対して JavaScript を実行するようなことも可能となっています。この JavaScript の実行は execute_script メソッドによって行うことができます。

JavaScriptの実行例
driver.execute_script("JavaScriptの処理")

execute_script メソッドを利用することで、Selenium のみでは実現できない操作も JavaScript の実行によって実現することも可能となります。

例えば、Selenium にはページのスクロールを行うようなメソッドは用意されていませんが、JavaScript を利用すればページのスクロールを実行することが可能です。例えば下記はページを 100 px 分だけ下方向にスクロールする処理の例となります。

ページのスクロール
driver.execute_script("window.scrollBy(0, 100);")

このように、execute_script メソッドによって JavaScript を実行することで実現可能なウェブブラウザへの操作の幅を広げることができます。

スポンサーリンク

Selenium でのウェブアプリの操作例

Selenium の使い方の解説は以上となります。

最後に、ここまでの解説内容の復習の意味も込めて、Selenium でのウェブアプリの操作例を示していきたいと思います。

今回は Django を利用してウェブアプリを動作させ、そのウェブアプリに対する操作を Selenium から行なっていきたいと思います。Django は Python におけるウェブアプリ開発用フレームワークの1つで、Django を利用することで簡単にウェブアプリを開発することができます。Django 自体については下記ページで解説していますので、興味がある人は読んでみていただければと思います。

【Django入門1】Djangoとは?

ちょっと Django でウェブアプリを用意するのが面倒かもしれませんが、実際にネット上で公開されているウェブアプリの操作を行なったりするとサーバーに負荷がかかって運営者や利用者に迷惑をかけてしまう可能性があります。ですが、ここから説明する方法で用意するウェブアプリは自身の PC 上で動作することになるため、好きなだけ Selenim を利用した操作や動作確認を行うことができます。

また、今回は Django で開発したウェブアプリというよりは、Django のウェブアプリに予め用意される管理画面に対して Selenium からの操作を実施していきます。この管理画面では、ウェブアプリのユーザーの登録や削除などを実施することが可能で、これらを Selenium を利用して Python スクリプトから実行していく例を示していきます。また、管理画面を利用するためには事前にログインを行う必要もあります。これに関しても Selenium を利用して実現していきます。

ウェブアプリの準備

ということで、ウェブアプリを Django で準備していきたいと思います。といっても、今回は Django でプロジェクトを作成すると自動的に用意される管理画面を利用するため、コマンドをいくつか実行するだけで準備は完了となります。

Django のインストール

まずは、Django をインストールしておきましょう!

Python を利用しているのであれば Django は pip からインストールすることが可能です。Django をインストールする際の pip の実行例は下記のようになります。

% python -m pip install django

ウェブアプリの用意

続いてウェブアプリを用意していきます。まずは適当な作業フォルダに移動してから下記コマンドを実行してください。実行すれば selenium_test というフォルダが作成され、その中にウェブアプリを開発するための様々な Python スクリプトファイルが生成されることになります。

% django-admin startproject selenium_test

次に、上記のコマンドによって生成された selenium_test フォルダの中に cd コマンドで移動します。

% cd selenium_test

そして、移動後に下記のコマンドを実行してください。このコマンドはウェブアプリから利用するデータベースを作成するためのコマンドとなります。

% python manage.py migrate

ウェブアプリ自体の用意は以上で完了です。

本来であれば、上記のようなコマンドを実行した後にウェブアプリの作り込みを行なっていく必要があるのですが、前述でも少し触れた通り今回は Django のウェブアプリに用意される管理画面を利用して Selenium での動作確認を実施していくことになり、管理画面自体は上記のコマンドで既に利用可能となっています。

ログイン用ユーザーの作成

ただし、管理画面にログインするためには事前にユーザーを作成しておく必要があります。そのため、次はログイン用のユーザーの作成を行なっていきます。

先ほどと同様に、selenium_test フォルダの中で下記のコマンドを実行してください。このコマンドは、Django で開発したウェブアプリの管理画面にログインするためのユーザーを作成するコマンドとなります。

% python manage.py createsuperuser

上記コマンドを実行すれば、ユーザー名とメールアドレスとパスワードの入力が促されることになります。パスワードに関しては、確認用に計2回入力する必要があります。

今回は、これらの情報として下記を入力するようにしてください。メールアドレスは今後は使わないですが、ユーザー名とパスワードに関してはログイン操作を Selenium から実行する際に使用することになります。

  • ユーザー名:Yamadahanako
  • メールアドレス:hanako@example.com
  • パスワード:yh123456

これらの情報の入力後(パスワードに関しては2回入力が必要)、下記のメッセージが表示されればユーザーの作成は完了となります。

Superuser created successfully.

ウェブアプリの起動

最後にウェブアプリを起動させます。先ほどと同様に、selenium_test フォルダの中で下記のコマンドを実行してください。

% python manage.py runserver

実行すると、下記のようなメッセージが表示されてウェブアプリが起動します。

% python manage.py runserver      
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
May 27, 2023 - 10:52:50
Django version 4.0.5, using settings 'selenium_test.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

そして、これによってウェブブラウザからウェブアプリの操作を行うことができるようになります。上記コマンドはずっと実行された状態となっているため、コマンドを終了させたい場合は ctrl + c キーの入力を行ってください。コマンドを終了させればウェブアプリも終了することになります。再度ウェブアプリを起動する場合は、上記と同じコマンドを実行すれば良いです。

手動でのウェブアプリの操作

続いて、先ほど用意したウェブアプリを手動で操作していきましょう!

ここで実行した手動での操作を、次のフェーズでは Selenium から実行するようにしていきます。ですので、各種操作を実現するために Selenium でどんな処理を実行する必要があるかを思い浮かべながら操作を行なっていくようにしてください。

ページを開く(手動)

まず、ウェブブラウザを開き、ウェブブラウザの URL バーに下記の URL を指定してください。

http://localhost:8000/admin/

指定すれば、下図のようなログインフォームが表示されると思います。

URLの指定によって開かれるログインフォーム

ログイン操作を行う(手動)

このログインフォームには、先ほど作成したユーザーの情報を入力してください。つまり、Username フィールドには YamadaHanako を、Password フィールドには yh123456 を入力してやれば良いです。入力後、LOG IN ボタンをクリックしてログインを実施します。

ログインフォームへの操作を示す図

ログインに成功すれば、下の図のようなページに遷移すると思います。このページが Django のウェブアプリにおける管理画面となります。

ログイン後に表示される管理画面

ユーザー一覧を表示する(手動)

続いて、Users リンクをクリックしてください。このリンクをクリックすればユーザー一覧が表示されるはずです。

ユーザー一覧を表示するための操作

ユーザーを登録する(手動)

続いて、ユーザーの登録を行なっていきます。まずは、ユーザー一覧ページの右上にある ADD USER ボタンをクリックしてください。

ユーザー登録を行うための操作1

クリックすると、下の図のようなユーザー登録フォームが表示されると思います。

ユーザー登録を行うための操作2

続いて、このフォームの Usename フィールドに YamadaTaro を、Password フィールドと Password confirmation フィールドの両方に yt123456 を入力して SAVE ボタンをクリックしてください。

ユーザー登録を行うための操作2

これによりユーザーが登録され、続いてユーザーの詳細情報入力フォームが表示されることになります。ページを下方向にスクロールしていけば、下の図のような Staff status チェックボックスが表示されるはずなので、このチェックボックスのチェックを ON にしてください。

ユーザー登録を行うための操作3

そして、ページの一番下にある SAVE ボタンをクリックし、先ほどのチェックボックスの変更内容をユーザーの情報に反映させます。

ユーザー登録を行うための操作4

SAVE ボタンをクリックすれば、再びユーザー一覧ページが表示されると思います。そして、ユーザー一覧の中に、先ほど登録したユーザーが表示されるようになったことが確認できると思います。また、ユーザーを追加で登録したい場合は、ADD USER ボタンをクリックして先ほどと同様の手順を踏めば良いことになります。

ユーザーを削除する(手動)

続いて、ユーザーの削除を行なっていきます。ユーザーの削除は、今表示されているユーザー一覧ページから行うことができます。

まず、ユーザー一覧の中から削除したいユーザーを選択します。今回は、先ほど登録した YamadaTaro を削除するため、YamdaTaro のチェックボックスをクリックしてチェックを ON にしてください。

ユーザー削除のための操作1

続いて、Action フィールドをクリックし、表示される選択肢の中から Delete selected users をクリックして選択します。そして、その次に、そのフィールドの右にある Go ボタンをクリックします。

ユーザー削除のための操作2

Go ボタンをクリックすると、下の図のような確認ページが表示されるため、Yes, I'm sure ボタンをクリックし、削除を実行しましょう!

ユーザー削除のための操作3

そうすると、再びユーザー一覧ページに遷移することになるはずです。そして、先ほど削除したユーザーがユーザー一覧から消えていることが確認できるはずです。

ユーザーが削除された様子

ログアウトする(手動)

最後に管理画面からのログアウトを行なっておきましょう!

ログアウトは、ページの上部にある LOG OUT リンクをクリックすることで行うことができます。

ログアウトのための操作

以上が、Django の管理画面からユーザーの追加や削除を行う一連の流れとなります。次は、ここで行なった操作を Selenium で実現していきたいと思います。

様々な操作を行いましたが、大体どんな処理を実行して行けば良いのかは想像がついているのではないかと思います。

スポンサーリンク

Selenium でのウェブアプリの操作

ということで、先ほど実施した手動での操作を、次は Selenium を利用した Python スクリプトから実行していきたいと思います。

Selenium の使い方 で説明したように、特にページ内の要素の操作を行う際には要素の探索が重要となります。この要素の探索を行う際に、要素を特定するための情報を調べる必要がありますので、Chrome で検証機能を利用しながらスクリプトを実装していくことになります。

ページを開く(Selenium)

まず行うのはページを開く操作になります。このページを開く操作は get メソッドで実現できましたね!ただし、ウェブブラウザを操作するためには事前に WebDriver のサブクラスのインスタンスを生成しておく必要があります。また、必要なモジュールの import も必要です。

そのため、まずは Python スクリプトに下記のような処理を記述することになります。

ページを開く
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()

driver.get('http://localhost:8000/admin/')

上記のソースコードでは、まず webdriver.Chrome() で Chrome と Chrome ドライバを起動し、続いて driver.get('http://localhost:8000/admin/') で引数に指定した URL を開く処理が Chrome に対して実行されることになります。

ログイン操作を行う(Selenium)

続いてログイン操作を Selenium で実現していきます。

ログインフォームは下の図のようになっており、Username フィールドには YamadaHanako を、Password フィールドには yh123456 を入力する必要があります。そして、その後に LOG IN ボタンをクリックすることでログインを実現することができます。

ログインフォームへの操作を示す図

取得した要素を操作する で説明したように、文字列の入力は send_key メソッド、クリックは click メソッドにより実現できます。ですが、メソッドを実行する前に、操作対象となる要素のインスタンスを find_element メソッドで取得する必要があります。そして、その取得したインスタンスから send_keyclick メソッドを実行させることになります。

また、今回は find_element では基本的に XPath での探索を行なって要素の取得を行うようにしていきたいと思います。

なので、ログイン操作を実現するための処理は、下記のような形式のものになることになります。まずは下記を、先ほど紹介したソースコードの下側に追記してください。

ログイン操作
username_field = driver.find_element(by=By.XPATH, value='')
username_field.send_keys('YamadaHanako')

password_field = driver.find_element(by=By.XPATH, value='')
password_field.send_keys('yh123456')

login_button = driver.find_element(by=By.XPATH, value='')
login_button.click()

上記が実行されれば、find_element で XPath が value の値と一致する要素を取得し、その取得した要素に対して操作(文字列入力やクリック操作)が行われることになります。

ただし、現状では value の値が空文字列となっているため、この部分に各要素の XPath を調べ、その XPath 記述する必要があります。

ということで、次は XPath を調べていきたいと思います。

ページ内の要素を取得する でも説明したように、この XPath は Chrome の検証機能を利用すれば簡単に調べることができます。

まず、Chrome でログインフォームを表示してください。前述の通り、下記 URL を URL バーに指定してやれば、ログインフォームを表示することができます(ウェブアプリを起動しておく必要がある点&ログアウトしておく必要がある点に注意してください)。

http://localhost:8000/admin/

続いて、ログインフォームの Username フィールド(入力欄)を右クリックし、表示される右クリックメニューから 検証 を選択します。

すると、検証機能のタブが Chrome の画面右側に表示され、さらに先ほど右クリックを行った Username フィールドに対応するタグがハイライトされた状態で表示されていると思います(タグが表示されるのは検証機能の Elements タブになります)。

XPathの取得手順1

続いて、そのタグを右クリックし、表示されるメニューから コピー を、さらに コピー を選択することで表示されるメニューから XPath をコピー を選択してください。これにより、Username フィールドの XPath がクリップボードにコピーされるはずなので、続いて先ほど示したソースコードにおける下記行の value='''' の間に貼り付けを行なってください。

Usenameの取得部分
username_field = driver.find_element(by=By.XPATH, value='')

おそらくですが、この貼り付け手順を実施することで、上記の行は次のように変化することになるはずです。

Usenameの取得部分
username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')

以上が、1つの要素の XPath を取得する手順となります。あとは、同様にして Password フィールドと LOG IN ボタンの XPath を取得し、ソースコードへの貼り付けを行なってください。

貼り付け後のソースコードは下記のようなものになると思います。使用している Django のバージョン等によって XPath が異なるものになる可能性もあるので、一応自身の手で XPath を貼り付けすることをオススメします。

ログイン操作
username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
username_field.send_keys('YamadaHanako')

password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password"]')
password_field.send_keys('yh123456')

login_button = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')
login_button.click()

以上で、管理画面へのログイン操作を行う処理は完成となります。

ユーザー一覧を表示する(Selenium)

先ほどのログイン操作を行う処理が実行されれば管理画面にログインされることになるため、次は管理画面でユーザーの登録やユーザーの削除を行なっていきましょう!

まず、Selenium から管理画面のトップページにある Users リンクをクリックしてしてユーザー一覧を表示していきます。

ユーザー一覧を表示するための操作

といっても、やることは先ほどとほぼ同じで、Users リンクの XPath を Chrome からコピーし、そのコピーした XPath の要素を find_element で探索して取得、さらにはその取得した要素に対してクリック操作を行うような処理を記述してやれば、Users リンクのクリックを実現することができます。

そして、これらの処理は下記のようなものになると思います。XPath には私が Chrome からコピーしたものを記載していますが、もしかしたら異なる可能性もあるため、念の為自身でコピーしたもので上書きしておいていただいた方が無難だと思います(以降も私が取得した XPath を載せていきますが、同様に自身で Chrome から取得したものに置き換えるようにしてください)。

Usersリンクのクリック
users_link = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/div/table/tbody/tr[2]/th/a')
users_link.click()

ユーザーを登録する(Selenium)

続いて、ユーザー登録を行なっていきます。ユーザー登録を行う際には、まず先ほどの処理によって表示されるユーザー一覧ページの右上にある ADD USER ボタンをクリックする必要があります。

ユーザー登録を行うための操作1

このクリック操作に関しては、下記のような処理で実現することができます。

ADD USERボタンのクリック
add_user_button = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/ul/li/a')
add_user_button.click()

さらに、ADD USER ボタンをクリックすればユーザー登録フォームが表示されますので、このフォームの Username フィールドに YamadaTaroPassword フィールドと Password confirmation フィールドに yt123456 を入力し、その後 SAVE ボタンをクリックするという一連の操作の実装を行なっていきます。

ユーザー登録を行うための操作2

この辺りもログイン操作時とほとんど同じ処理で実現することができます。具体的には下記のような処理によってユーザー登録を実現することができます。

ユーザーの登録
username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
username_field.send_keys('YamadaTaro')

password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password1"]')
password_field.send_keys('yt123456')

password2_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password2"]')
password2_field.send_keys('yt123456')

save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
save_button.click()

上記のような処理によってユーザーが登録されれば、次はユーザーの詳細情報の設定フォームが表示されることになります。ここでは、Staff status のチェックボックスを ON に変更し、その後 SAVE ボタンをクリックして詳細情報の変更の反映を行います。

ユーザー登録を行うための操作3

このチェックボックスの ON / OFF に関してもクリック操作で実現できますので、ここまで行なってきたリンクのクリックやボタンのクリック同様に、click メソッドを利用することになります。

Staff statusのチェックボックスのON
is_staff_check = driver.find_element(by=By.XPATH, value='//*[@id="id_is_staff"]')
is_staff_check.click()

save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
save_button.click()

上記の最後の行での SAVE ボタンのクリックが実行されれば、ユーザーの情報が更新された後にユーザー一覧表示ページに遷移することになります。

ユーザーを削除する(Selenium)

次は、先ほど登録したユーザー YamadaTaro の削除を行なっていきます。ユーザーの削除は、ユーザー一覧の中で削除したいユーザーにチェックを入れ、

ユーザー削除のための操作1

Action リストの中から Delete selected users を選択したのちに Go ボタンをクリックすることで実現することができます。

ユーザー削除のための操作2

これも今まで通りのやり方で Selenium からの操作を実現することはできるのですが、注意点があるのでその点について説明したいと思います。

注意点とは、ユーザー一覧に表示されるユーザーの順番になります。この Django の管理画面においては、デフォルトではユーザー一覧に表示されるユーザーはユーザー名に対してアルファベット順に並んでいます。最初に createsuperuser コマンドで YamadaHanako を作成し、管理画面では YamadaTaro を作成したため、ユーザー一覧には上側に YamadaHanako、下側に YamadaTaro が表示されることになります。

要素の表示位置によってXPathが異なる様子1

この場合、YamadaTaro のチェックボックスの XPath は下記のようなものになると思います。

//*[@id="result_list"]/tbody/tr[2]/td[1]/input

ですが、createsuperuser コマンドで作成するユーザーのユーザー名に YamadaTaro よりもアルファベット順的に遅い名前、例えば Zack などを設定していた場合、ユーザー一覧には YamadaTaro が上側に表示されることになります。

要素の表示位置によってXPathが異なる様子2

そして、この場合、YamadaTaro のチェックボックスの XPath は下記のようなものになります。

//*[@id="result_list"]/tbody/tr[1]/td[1]/input

前述で示した YamadaTaro のチェックボックスの XPath では tr[2] であった部分が tr[1] に変化していることが確認できると思います。また、この場合の Zack のチェックボックスの XPath は下記となり、これが前述の YamadaTaro の XPath と一致していることになります。

//*[@id="result_list"]/tbody/tr[2]/td[1]/input

つまり、YamadaTaro というユーザーを削除しようと思ってチェックボックスの XPath を find_element メソッドの value 引数に指定していたとしても、他のユーザーのユーザー名によっては、その XPath が他のユーザーのチェックボックスに対応している可能性があります。そして、この場合、YamadaTaro を削除しようとしているのに他のユーザーを削除してしまうことになります。

ここで重要な点は、あくまでも XPath は HTML の構造やタグの位置によって決定される情報であるという点になります。したがって、状況によって HTML の構造や要素の位置が変化してしまうような場合、その状況によっては Selenium で実現したい操作を行えないようなケースがあります。例えば前述のユーザー一覧に関しては、ユーザーの登録状況によって削除したいユーザーのチェックボックス要素の位置が変化することになるため、意図しないユーザーの削除が実行されてしまう可能性があります。

以前試した時から削除対象が変わってしまう様子

もちろん、今回は登録済みのユーザーが YamadaHanakoYamadaTaro の2人であることを前提としているため、XPath での探索で YamadaTaro のチェックボックスを取得すれば上手く動作するのですが、逆に言えば、その前提が崩れると上手く動作しなくてしまうことになります。これだと応用性が低く使い勝手が悪いです。

では、ユーザーの登録状況に関わらず、YamadaTaro を確実に削除するにはどうすれば良いでしょうか?

やり方は様々ありますが、今回は YamadaTaro というユーザー名を持つユーザーを削除しようとしているのですから、このユーザー名を頼りにチェックボックス要素の取得を行なうことで確実に YamadaTaro を削除することができます。

まず、この管理画面のユーザー一覧の HTML の構造に着目すると、このユーザー一覧は下の図のような列数 6 のテーブルとして表現されています。そして、このテーブルの各行に各ユーザーの情報が格納されて表示されるようになっています。

ユーザーの情報がテーブル形式で表示されていることを示す図

この、テーブルにおける行は、HTML としては tr タグで表現されます。つまり、1つの <tr> 〜 </tr> の中に一人のユーザーの情報が格納されています。さらに、行の中の各要素は tdth タグで表現されます。実際に、この tr タグの中身を Chrome の検証機能で表示した結果が下の図となります。

tr要素の中身を示す図

そして、この 1 つ目の th タグの中にユーザー名が存在し、さらに 1 つ目の td タグの中に削除対象を選択するためのチェックボックスが存在しています。

tr要素の1つ目のtd要素内にチェックボックスが存在し、1つ目のth要素内にチェックボックスが存在することを示す図

したがって、このテーブルの中の tr タグの要素を1つ1つ取得し、各  tr タグの要素の 1 つ目の th タグの本文が YamadaTaro であるかどうかをまず判断し、判断結果が Yes の場合に 1 つ目の td タグの中のチェックボックスに対してクリック操作を行うようにすれば、確実に YamadaTaro のチェックボックスのみを ON とすることができます。

削除したいユーザーを確実に削除する手順

では、このような処理はどうやって実現できるのか?について考えていきたいと思います。先程も紹介しましたが、各ユーザーのチェックボックスの XPath は下記のような形式のものになっています。

//*[@id="result_list"]/tbody/tr[1]/td[1]/input
//*[@id="result_list"]/tbody/tr[2]/td[1]/input

ここまでの解説を踏まえると、上記の XPath は次のようなものであると考えられます。

まず、上記における tr[1]tr[2] 部分はテーブルの各行を表しています。tr[1] が1行目で、一人目のユーザーの情報を表示しており、tr[2] が2行目で、二人目のユーザーの情報を表示しています。

まず取得すべき要素は、これらの tr 要素となります。ただし、ここで取得したいのはテーブル内の全ての tr 要素となるため、複数の要素が取得できるように find_elements メソッドを利用します。

そして、下記のように引数を指定して find_elements メソッドを実行します。

全tr要素の取得
trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')

ポイントは、指定している XPath における最後の tr 部分になります。

Chrome からコピーされる XPath は、その要素を一意に特定するための情報であるため、tr[1]tr[2] のようにタグ名に対してインデックスが指定されるようになっています。XPath において、インデックスは 1 から始まります。

このようなインデックスの指定により、複数の同種類のタグの要素の中から1つを探索することができるのですが、1つのみではなく全ての要素を取得したい場合は、上記のように value にはインデックスを指定せずに find_elements を実行するようにしてやれば良いです。

上記の場合であれば、//*[@id="result_list"]/tbody/ という XPath に対応する要素の中の全ての tr 要素が取得されることになります。ちなみに、ワイルドカードを利用して同様の処理を行うことも可能です。ワイルドカードを利用した場合は、value//*[@id="result_list"]/tbody/tr[*] を指定することになります。これでも同じ結果が得られます。

さらに、find_elements では取得した各要素が格納されたリストが返却されることになるので、このリストから1つ1つ要素を取得し、その要素の中の th 要素の本文が YamadaTaro であるかどうかを確認していくことになります。この際には、tr 要素の中から th 要素を取得することになるため、下記のように for 文の中で th 要素の取得を行う必要があります。この時は 1 つ目の th 要素を取得したいため、value としては th[1] のようにインデックスを指定して find_element によって取得を行う必要があります。 

1つ目のthの取得
trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')
for tr in trs:
    th = tr.find_element(by=By.XPATH, value='th[1]')
    if th.text == 'YamadaTaro':

このように、find_elementdriverからだけではなく、find_elementfind_elements で取得した要素からも実行することが可能です(find_elements も同様です)。そして、これにより、特定の要素を段階的に絞り込んで取得することが可能となります。

上記のような処理により、th[1] の本文(text データ属性)が 'YamadaTaro' と一致する tr 要素を見つけ出すことができるようになります。あとは、'YamadaTaro' と一致する tr 要素が見つかった際に、その tr 要素の持つ 1 つ目の td の中の input 要素をクリックしてやれば良いことになります。

YamadaTaroのチェックボックスをON
trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')
for tr in trs:
    th = tr.find_element(by=By.XPATH, value='th[1]')
    if th.text == 'YamadaTaro':
        check_box = tr.find_element(by=By.XPATH, value='td[1]/input')
        check_box.click()

上記の処理によって、YamadaTaro のチェックボックスが ON にされるようになるため、あとは Action リストから Delete selected users をクリックして選択し、Go ボタンを押せば良いだけです。これらの要素の位置はユーザーの登録状況に関わらず同じ場所に存在するため、単に XPath から探索を行えば良いです。

ユーザー削除のための操作2

ただし、Action リストの Delete selected users の選択肢はページ上に表示されていないため、検証機能によって表示される HTML の中から該当するタグを見つけ出し、そのタグを右クリックして XPath をコピーする必要があります。また、実際に手作業でユーザーを削除する場合は、一度 Action リストをクリックして選択肢を表示し、その後に Delete selected users をクリックすることになるのですが、Selenium の場合は Action リストのクリックを行わずに直接 Delete selected users をクリックするのでも問題ありません。

手作業の時に Action リストをクリックする必要があったのはページ上に表示されていない選択肢 Delete selected users を表示するためです。が、HTML 上には Delete selected users がページ表示時から存在していますので、わざわざページ上に表示するためのクリック操作は必ずしも行う必要はないです(必須ではないだけで、別に行なっても良いです)。

実際に XPath をコピーして上記の処理を実装したソースコードは次のようなものになります。

選択ユーザーの削除
delete_selected_users = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/label/select/option[2]')
delete_selected_users.click()

go_button = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/button')
go_button.click()

上記の処理によって Go ボタンがクリックされれば、次は削除の確認ページに遷移することになります。ここでは Yes, I'm sure ボタンのクリックを行うことになります。

ユーザー削除のための操作3

ただ、ここもユーザー一覧から削除するユーザーを選んだ時と同様の点に注意が必要となります。今回は YamadaTaro の一人のみを削除するため問題はないのですが、複数のユーザーを削除しようとした場合、その削除対象にユーザーの数によって Yes, I'm sure ボタンの XPath が変化することになります。そのため、複数人のユーザー削除に対応しようと思うと少し工夫が必要になります。

この Yes, I'm sure ボタンの XPath は YamadaTaro の一人のみを削除しようとした場合は下記のようになります。

//*[@id="content"]/form/div/input[4]

それに対し、4人のユーザーを削除しようとした場合は下記のようになります。

//*[@id="content"]/form/div/input[7]

要は、XPath の最後の input の添字が削除しようとしてるユーザーの人数 +3 になることになります。これはつまり、削除するユーザーの数だけ Yes, I'm sure ボタンよりも上側に input 要素が追加されていることを示しています。

ただ、幸いなことに、input 要素が追加されのは Yes, I'm sure ボタンよりも上側のみです。つまり、先頭の input 要素の位置から考えると Yes, I'm sure ボタンの位置が削除するユーザーの数によって変化することになりますが、末尾の input 要素の位置から考えると Yes, I'm sure ボタンの位置は変化しません。もっと言えば、Yes, I'm sure ボタンは常に input 要素の中の末尾に存在します(実は、隣にある No, take me back ボタンは Yes, I'm sure ボタン同様に input 要素の見た目をしていますが、実態はただのリンクです)。

であれば、XPath の input のインデックスには末尾を示すものを指定してやれば良さそうです。

そして、XPath では末尾の要素を指定するための last() が利用可能です。これは、同列に並んだ要素の中の末尾のものを指定する関数となります。したがって、Yes, I'm sure ボタンを探索する際には、下記の XPath を指定してやれば良いことになります。

//*[@id="content"]/form/div/input[last()]

そして、この考え方に基づいた Yes, I'm sure ボタンのクリック操作は下記のような処理で実現することができます。

Yes, I'm sure ボタンのクリック
sure_button = driver.find_element(by=By.XPATH, value='//*[@id="content"]/form/div/input[last()]')
sure_button.click()

もちろん実現方法は他に持って、削除するユーザーの選択を行った時のように、全ての input 要素を取得し、その中から本文が Yes, I'm sure であるものを見つけ出すのでも良いです。とにかく大事なのは、目当ての要素を1つに特定できるように要素の探索を行うことであり、具体的な方法は何でも良いと思います。

ログアウトを行う(Selenium)

この章の最後としてログアウトを行なっていきたいと思います。

ログアウトはページ上部の LOG OUT リンクのクリックにより実現できます。

ログアウトのための操作

そして、これは LOG OUT リンクの要素を取得したのちにクリック操作を行えば良いだけです。

ログアウトの実行
logout_link = driver.find_element(by=By.XPATH, value='//*[@id="user-tools"]/a[3]')
logout_link.click()

全体のソースコード

ここまでのまとめの意味も込めて、紹介してきたソースコードを1つにまとめたものを下記に示しておきます。

管理画面での操作
from selenium import webdriver
from selenium.webdriver.common.by import By

# ブラウザの起動
driver = webdriver.Chrome()

# 管理画面の表示
driver.get('http://localhost:8000/admin/')

# ログイン
username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
username_field.send_keys('YamadaHanako')

password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password"]')
password_field.send_keys('yh123456')

login_button = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')
login_button.click()

# ユーザー一覧表示
users_link = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/div/table/tbody/tr[2]/th/a')
users_link.click()

# ユーザー追加
add_user_button = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/ul/li/a')
add_user_button.click()

username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
username_field.send_keys('YamadaTaro')

password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password1"]')
password_field.send_keys('Taro12345')

password2_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password2"]')
password2_field.send_keys('Taro12345')

save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
save_button.click()

is_staff_check = driver.find_element(by=By.XPATH, value='//*[@id="id_is_staff"]')
is_staff_check.click()

save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
save_button.click()

# ユーザーの削除
trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')
for tr in trs:
    th = tr.find_element(by=By.XPATH, value='th[1]')
    if th.text == 'YamadaTaro':
        check_box = tr.find_element(by=By.XPATH, value='td[1]/input')
        check_box.click()

delete_selected_users = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/label/select/option[2]')
delete_selected_users.click()

go_button = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/button')
go_button.click()

sure_button = driver.find_element(by=By.XPATH, value='//*[@id="content"]/form/div/input[last()]')
sure_button.click()

# ログアウト
logout_link = driver.find_element(by=By.XPATH, value='//*[@id="user-tools"]/a[3]')
logout_link.click()

# ブラウザの終了
driver.quit()

実行すれば、手動で操作した時と同様に、ログイン操作が行われたのちにユーザーの追加とユーザーの削除が実行され、最後にログアウトが行われたのちにブラウザが終了することになります。

実行時の注意点としては、この管理画面では同じユーザー名のユーザーの作成は不可である点が挙げられます。このスクリプトでは YamadaTaro  というユーザー名のユーザーを作成するため、既に YamadaTaro が作成されている状態で実行すると途中で例外が発生して処理が終了することになります。

応用例1:操作のループ

また、上記の各種操作を関数化することで、複数のユーザーをループで一気に登録するようなことも可能となります。その例が下記となり、このスクリプトでは 20 人のユーザーの登録を行い、最後に YamadaHanako 以外のユーザーを削除する処理を行なっています。

複数ユーザーの登録と削除
from selenium import webdriver
from selenium.webdriver.common.by import By

def login(username, password):
    username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
    username_field.send_keys(username)

    password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password"]')
    password_field.send_keys(password)

    login_button = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')
    login_button.click()

def show_users():
    users_link = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/div/table/tbody/tr[2]/th/a')
    users_link.click()

def add_user(username, password, is_staff):
    add_user_button = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/ul/li/a')
    add_user_button.click()

    username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
    username_field.send_keys(username)

    password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password1"]')
    password_field.send_keys(password)

    password2_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password2"]')
    password2_field.send_keys(password)

    save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
    save_button.click()

    if is_staff:
        is_staff_check = driver.find_element(by=By.XPATH, value='//*[@id="id_is_staff"]')
        is_staff_check.click()

    save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
    save_button.click()

def delete_users():
    trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')
    for tr in trs:
        th = tr.find_element(by=By.XPATH, value='th[1]')
        if th.text != 'YamadaHanako':
            check_box = tr.find_element(by=By.XPATH, value='td[1]/input')
            check_box.click()

    delete_selected_users = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/label/select/option[2]')
    delete_selected_users.click()

    go_button = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/button')
    go_button.click()

    sure_button = driver.find_element(by=By.XPATH, value='//*[@id="content"]/form/div/input[last()]')
    sure_button.click()

def logout():
    logout_link = driver.find_element(by=By.XPATH, value='//*[@id="user-tools"]/a[3]')
    logout_link.click()



# ブラウザの起動
driver = webdriver.Chrome()

# 管理画面の表示
driver.get('http://localhost:8000/admin/')

login('YamadaHanako', 'yh123456')

show_users()

for i in range(20):
    username = 'YamadaTaro_' + str(i)
    add_user(username, 'yt123456', True)

delete_users()

logout()

# ブラウザの終了
driver.quit()

ウェブアプリの動作確認を行う際、事前に複数のユーザーを登録した状態で動作確認を実施したいような場合も多いと思いますので、そのような時に上記のスクリプトのユーザーを作成する処理の部分が利用できると思います。

このように、1つの対象に対する操作を Selenium で実現できるようにしてやれば、ループを利用して拡張を行うことで自動的に複数の対象に対して操作を実施できるようなスクリプトを簡単に実現することができます。

応用例2:スクリーンショット撮影

また、Selenium からはページのスクリーンショットを撮影することも可能です。

下記は各種操作を行う前後にスクリーンショットを撮影する例になります。実行すると多くの画像が保存されることになるので、その点にはご注意ください。また、ヘッドレスモードでウェブブラウザを起動するようにしているため、実行してもウェブブラウザは画面上に表示されません。が、スクリーンショットはバッチリ撮影されていくはずです。

操作の前後でのスクリーンショット撮影
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options

def login(username, password):
    driver.save_screenshot('before_login.png')

    username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
    username_field.send_keys(username)

    password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password"]')
    password_field.send_keys(password)

    login_button = driver.find_element(by=By.XPATH, value='//*[@id="login-form"]/div[3]/input')
    login_button.click()

    driver.save_screenshot('after_login.png')

def show_users():
    driver.save_screenshot('before_show_users.png')

    users_link = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/div/table/tbody/tr[2]/th/a')
    users_link.click()

    driver.save_screenshot('after_show_users.png')

def add_user(username, password, is_staff):
    driver.save_screenshot('before_add_user_{}.png'.format(username))

    add_user_button = driver.find_element(by=By.XPATH, value='//*[@id="content-main"]/ul/li/a')
    add_user_button.click()

    username_field = driver.find_element(by=By.XPATH, value='//*[@id="id_username"]')
    username_field.send_keys(username)

    password_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password1"]')
    password_field.send_keys(password)

    password2_field = driver.find_element(by=By.XPATH, value='//*[@id="id_password2"]')
    password2_field.send_keys(password)

    save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
    save_button.click()

    if is_staff:
        is_staff_check = driver.find_element(by=By.XPATH, value='//*[@id="id_is_staff"]')
        is_staff_check.click()

    save_button = driver.find_element(by=By.XPATH, value='//*[@id="user_form"]/div/div/input[1]')
    save_button.click()

    driver.save_screenshot('after_add_user_{}.png'.format(username))

def delete_users():
    trs = driver.find_elements(by=By.XPATH, value='//*[@id="result_list"]/tbody/tr')
    for tr in trs:
        th = tr.find_element(by=By.XPATH, value='th[1]')
        if th.text != 'YamadaHanako':

            driver.save_screenshot('before_check_user_{}.png'.format(th.text))

            check_box = tr.find_element(by=By.XPATH, value='td[1]/input')
            check_box.click()

            driver.save_screenshot('after_check_user_{}.png'.format(th.text))

    driver.save_screenshot('before_delete_users.png')

    delete_selected_users = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/label/select/option[2]')
    delete_selected_users.click()

    go_button = driver.find_element(by=By.XPATH, value='//*[@id="changelist-form"]/div[1]/button')
    go_button.click()

    sure_button = driver.find_element(by=By.XPATH, value='//*[@id="content"]/form/div/input[last()]')
    sure_button.click()

    driver.save_screenshot('after_delete_users.png')

def logout():
    driver.save_screenshot('before_logout.png')

    logout_link = driver.find_element(by=By.XPATH, value='//*[@id="user-tools"]/a[3]')
    logout_link.click()

    driver.save_screenshot('after_logout.png')


options = Options()
options.add_argument('--headless')

# ブラウザの起動
driver = webdriver.Chrome(options=options)

# 管理画面の表示
driver.get('http://localhost:8000/admin/')

login('YamadaHanako', 'yh123456')

show_users()

for i in range(20):
    username = 'YamadaTaro_' + str(i)
    add_user(username, 'yt123456', True)

delete_users()

logout()

# ブラウザの終了
driver.quit()

Selenium で操作を行わせると目にも止まらぬスピードで操作が実行されていくため、何が行われているかが把握しづらいですが、各種操作の前後でスクリーンショット撮影を行うようにしておけば、後から撮影したスクリーンショットを調べ、意図した通りの操作が行われているかどうかを確認するようなこともできます。

まとめ

このページでは Python からの Selenium の使い方について、特にウェブブラウザ・ウェブページへの操作に焦点を当てて解説を行いました!

基本的な Selenium の利用手順は下記となります。

  1. WebDriver のサブクラスのインスタンスを生成する
    • ウェブブラウザのドライバとウェブブラウザが起動する
  2. ページを表示する
  3. ページ内の要素を取得する
  4. 取得した要素に対して操作を行う
    (3. と 4. は必要な分だけ繰り返す)
  5. ウェブブラウザを終了させる

特に、Selenium からウェブブラウザを操作する際にポイントになるのが要素を取得する点になると思います。そして、この要素を取得する際には要素の探索条件をうまく設定する必要があり、ここが Selenim を扱う上で一番難しいところになるかと思います。

ですが、Chrome 等の検証機能を利用すれば、要素を特定するための条件、例えば XPath などは簡単に取得することができます。そのため、Selenium でウェブブラウザの操作を行う際にはこういった機能を利用することをオススメします!

また、Selenium でのウェブブラウザの操作を応用することで、手作業で行っていたウェブアプリの動作確認も自動的に実行することができるようになります。是非、このページで解説した内容を活かして、業務や作業の自動化・効率化に取り組んでいただければと思います!

同じカテゴリのページ一覧を表示