初めに
Windowsで面倒な環境構築をせずにSeleniumでスクレイピングがしたい。
最近はWSL2でdockerコンテナを動かせば簡単にseleniumの環境構築もできると思いますが、会社の環境だとできなかったりするのでもっとお手軽にやりたいと思いました。
というわけで調べたところPowerShell用のSeleniumモジュールがあったので使用してみることに。
大まかな使いかたをまとめました。
私自身それほどSeleniumに関して詳しくないので使用していてわかった事があれば
随時追記していこうと思います。
もっと良いコマンドの使用方法など指摘あればコメント頂けると幸いです。
環境
PowerShellのバージョン
$PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.1
PSEdition Desktop
...
Seleniumモジュールのバージョン
Get-InstalledModule -Name selenium
Version Name Repository Description
------- ---- ---------- -----------
3.0.1 Selenium PSGallery Web automation using the Selenium Web...
現在(2020年10月)時点ではバージョン3.0.1が最新ですがもう少しでバージョン4が出そうです。
バージョン4が出たら随時更新したいと思います。
Chromeのバージョン
実際に使ってみる
モジュールのインストール
下記コマンドで最新のモジュールをインストールできます。
インストールさえしてしまえばもうSeleniumでスクレイピングできます。
Install-Module -Name Selenium
ブラウザを起動する
起動コマンドレットとそのパラメーター
ブラウザを起動するにはStart-SeChrome
などStart-Se<ブラウザ名>
を使用します。
下記は主に使うパラメーターを抜粋しています。
Start-SeChrome
[-Headless] #ブラウザ非表示
[-Maximized] #全画面
[-DefaultDownloadPath <String>] #ダウンロード時のパス指定
[-StartURL <String>] #起動時に開くURL
基本的な仕様
ブラウザ起動時には$Driver = Start-SeChrome
として変数$Driver
へ格納しますが
これは他のコマンドレットのパラメーターでドライバーを指定する際に必須となるので
決まり文句としてどのブラウザを使用する場合でも実施しています。
$Driver
は別ページへ推移したりブラウザの更新があると勝手に更新されます。(仮に手動で別ページへ推移させたとしても変数は勝手に更新されます。)
ブラウザの更新が完了した時点でのHTMLが自動取得されるので、更新を待つようなループ処理を作成する必要がありません。
各ブラウザ毎のコマンドレット
Chromeを使用する場合
$Driver = Start-SeChrome
Edgeを使用する場合
$Driver = Start-SeEdge
Firefoxを使用する場合
$Driver = Start-SeFirefox
IEを使用する場合
$Driver = Start-SeInternetExplorer
ここではまった事
いつも通り$Driver = Start-SeChrome
を実行するとエラーになりました。
Chromeをアップデートした後に発生したのでバージョンを確認して、対応するChromeDriverをダウンロードしなおしたら正常に動作しました。
ダウンロードはこちら
どうやらChromeのバージョンとChromeDriverのバージョンはそろえる必要があるようです。
私の環境では下記にのドライバーを更新しました。
C:\Users\ユーザ名\Documents\WindowsPowerShell\Modules\Selenium\3.0.1\assemblies\chromedriver.exe
指定のURLへ移動
Enter-SeUrl -Url '<URL>' -Driver $Driver
指定したURLへ推移し、ブラウザの更新が完了した時点の情報が$Driver
に格納されます。
エレメントの取得
エレメントの取得にはFind-SeElement
コマンドレットを使用します。
このコマンドレットは条件に合致したエレメントをオブジェクトの配列で返します。
(もしエレメントが見つからない場合は10秒でタイムアウトし、何も出力せず終了します。)
例えばdivをすべて取得する場合はFind-SeElement -Driver $Driver -TagName 'div'
とすればすべてのdivタグが配列として返されます。
配列で返されるので(Find-SeElement -Driver $Driver -TagName 'input')[3]
のようにインデックスでエレメントを指定もできます。
エレメントを取得した際に返されるオブジェクトのプロパティについて。
Find-SeElement -Driver $Driver -TagName div | ?{$_.Text -ceq 'ログイン'}
WrappedDriver : OpenQA.Selenium.Chrome.ChromeDriver
TagName : div
Text : ログイン
Enabled : True
Selected : False
Location : {X=0,Y=20}
Size : {Width=1261, Height=0}
Displayed : True
LocationOnScreenOnceScrolledIntoView : {X=0,Y=20}
Coordinates : OpenQA.Selenium.Remote.RemoteCoordinates
これらプロパティはPowerShellのその他のオブジェクトと同様にパイプラインで渡すことで、フィルタリングできます。(上記ではTextプロパティをWhere-Object
でフィルタリングしています。)
下記のようにさらに複雑な条件でフィルタリングできます。(ただし低速)
Find-SeElement -Driver $Driver -TagName div | ?{
$_.Displayed -eq $TRUE -and
$_.Text -ceq 'hogehoge' -and
$_.Location.X -eq 0
}
全てのエレメントを取得
Find-SeElement -Driver $Driver -TagName *
これは確かにすべてのエレメントを取得できますが、その後パイプラインで渡してWhere-Object
によるフィルタリングをする必要があります。それは非常に低速なのでお勧めしません。
下記に記載している各種パラメーターを使用し、ある程度フィルタリングした後にWhere-Object
によるフィルタリングをするのが無難と思われます。
XPathで取得
idやclassあるなしにかかわらずドンピシャでエレメントを指定できます。
idがあってもなくても正確に取得できますのでこれでほとんど事足りると思います。
Find-SeElement -Driver $Driver -XPath '<XPath>'
IDで取得
ドンピシャで指定できるのでidがある場合はこれを使用すればよいと思います。
Find-SeElement -Driver $Driver -Id '<ID>'
TagNameで取得
これで大雑把にフィルタリングしてから詳細な条件でフィルタリングしたい場合などによく使用します。
Find-SeElement -Driver $Driver -TagName '<Tagname>'
例えばliタグでテキストが「hogehoge」であるエレメントを取得したい場合は下記のようにします。
Find-SeElement -Driver $Driver -TagName 'li' | ?{$_.Text -ceq 'hogehoge'}
ClassNameで取得
Find-SeElement -Driver $Driver -ClassName 'hogehoge'
特定のテキストを含むエレメントを全て取得(低速なので非推奨)
Find-SeElement -Driver $Driver -TagName * | ?{$_.Text -ceq 'hogehoge'}
特定のテキストを含むdivを取得
Find-SeElement -Driver $Driver -TagName 'div' | ?{$_.Text -ceq 'hogehoge'}
特定の属性値を持つdivを取得
下記はclass属性の値が「hogehoge」であるエレメントを取得しています。
Find-SeElement -Driver $Driver -TagName div | ?{$_.GetAttribute('class') -ceq 'hogehoge'}
正規表現を使用したい場合は比較演算子を-match
とするだけです。-cmatch
としているのは大文字小文字を区別させるためです。
Find-SeElement -Driver $Driver -TagName div | ?{$_.GetAttribute('class') -cmatch '^hogehoge$'}
特定のエレメント配下のエレメントを取得
下記は配下の更に配下のように再帰的にすべてのエレメントを取得しているわけではありません。
あくまでもid="hogehoge"直下のエレメントのみ取得しています。
XPathでエレメントを指定してその配下を/*
としているだけです。もっといい方法がありそう。。
Find-SeElement -Driver $Driver -XPath '//*[@id="hogehoge"]/*'
取得したエレメントに対する操作
下記例にて記載されている$Element
には
$Element = Find-SeElement -Driver $Driver -Id '<ID>'
などで一つのエレメントが取得されているものとします。
取得したエレメントの属性値を取得する
下記はclass属性の値を返します。
$Element.GetAttribute('class')
キーボード入力
通常文字列
Send-SeKeys -Element $Element -Keys 'hogehoge'
特殊キー
下記はEnterキーの場合
Send-SeKeys -Element $Element -Keys '{{ENTER}}'
Send-SeKeys -Element $Element -Keys ([OpenQA.Selenium.Keys]::Enter)
クリックする
下記2つはどちらも通常のクリック。
同じ動作をするはず。。
$Element.Click()
$Element | Invoke-SeClick -Driver $Driver
JavaScriptでクリック
$Element | Invoke-SeClick -JavaScriptClick -Driver $Driver
スクリーンショットを保存する
$Screenshot = Invoke-SeScreenshot -Target $Driver
Save-SeScreenshot -Screenshot $Screenshot -Path ./test.png
-Path
パラメーターで任意のフォルダへ保存できます。
終了して閉じる
Stop-SeDriver -Driver $Driver