powershellでIE操作
powershellでIE操作しようとすると、私の環境では、DOMがまともに取り扱えません。Microsoft.mshtml のアンセンブリがないためらしく、MS officeをインストールする必要があるのだとか。参考そこで対策。ie.ps1#IEオートメーションを扱う際、microsoht.mshtml.dllが適切にインストールされていないシステムでは、#ドキュメントノードやエレメントのメソッドを呼び出す際、いちいちSystem.__ComObjectを介して#間接的にメソッドを呼ばなければならない。書式は複雑で手順も煩雑なため、IE操作のための関数を用意した。# IEの動作関係# openURL WaitIE WaitForElement IeActivate # System.__ComObjectを介するメソッド呼び出し# invokeComMethod # Dom要素取得# querySelectorAll querySelector getElementById # getElementsByName getElementsByTagName getElementsByClassName# Windowオブジェクトのpowershellから$ie.Document.parentWindowが取得できないのでWindow配下の# メソッド呼び出し・プロパティ読み書きを行う関数も用意する。# windowMethod windowProperty# 参考サイト https://qiita.com/voidProc/items/b3037ccb31676b27ceba###################################################################URLを開くfunction openURL( $url, $visible=$false ){ #IEオブジェクトの取得 try{ [Microsoft.VisualBasic.Interaction]::GetObject|out-null } catch { [reflection.assembly]::LoadWithPartialName("'Microsoft.VisualBasic")|out-null } try{ #ieが起動していればそのオブジェクトを取得 $ie =[Microsoft.VisualBasic.Interaction]::GetObject( "","InternetExplorer.Application" ) } catch { #ieオブジェクトを生成 $ie =new-object -com "InternetExplorer.Application" } $ie.Visible=$visible $ie.Navigate( $url ) return $ie}# IEのページ読み込みが完了するまで待機するfunction WaitIE( $ie, $OkSeconds=0.5 ){ # ビジーでない状態が$OkSeconds秒継続したら読み込み終わったと判断する $limit =( get-date ).AddSeconds( $OkSeconds ) do{ if (($ie.Busy) -or ( $ie.ReadyState -ne 4 )){ $limit =( get-date ).AddSeconds( $OkSeconds ) } sleep -m 100 }while( ( get-date ).CompareTo( $limit ) -lt 0 )}# 要素の出現を待つ(要素はCSSセレクタで指定)function WaitForElement( $ie, [string]$selector ){ WaitIE $ie while( $true ){ try{ if ( $e=(querySelector $ie.Document $selector) ){ break } }catch{[void]"err"} [void]"Loop" }}# このままで動くが、動作が不安定function _IeActivate{ #VB.NETのアセンブリを呼んでおく try{ [void][Microsoft.VisualBasic.Interaction] }catch{ Add-Type -AssemblyName "Microsoft.VisualBasic" } ps iexplore|%{ [Microsoft.VisualBasic.Interaction]::AppActivate($_.id) #ウエイトがないとアクティベートされないらしい。 sleep -m 200 }}# IEウインドウをアクティブにする# function IeActivate( $ie ){ windowMethod $ie "focus"}#####################Windowオブジェクト対策#windowオブジェクト配下のメソッド呼び出しfunction windowMethod{ $ie =$args[0] $methodName =$args[1] $others =if ( ($len=$args.length) -gt 2 ){ $args[2..($len-1)] } else { $null } $sc=new-object -com scriptcontrol $sc.language="vbscript" $sc.addobject("ie",$ie) $statement ="ie.document.parentwindow.$methodName(${others})" #"$statement" $sc.executestatement( $statement )}#ウインドウのプロパティの取得と代入 # 取得 windowProperty $ie "location.Href"# 代入 windowProperty $ie "location.Href" "https://www.infoseek.co.jp/"function windowProperty{ $ie =$args[0] $PropertyName =$args[1] $sc =new-object -com scriptcontrol $sc.language ="vbscript" $sc.addobject("ie",$ie) $statement ="ie.document.parentwindow.$PropertyName" #引数に二つ目があれば代入 if ( $val=$args[2] ){ $assignExpression ="$statement = `"$val`"" $sc.executestatement( $assignExpression ) return $val } else { return $sc.eval( $statement ) }}#####################Com上のメソッド呼び出し#function invokeComMethod( $obj, $methodName ){# return [System.__ComObject].InvokeMember( $methodName, [System.Reflection.BindingFlags]::InvokeMethod, $null, $obj, $args )#}#引数が上の呼び方だと、動かないことがある。#動かない例:invokeComMethod $親要素 "appendChild" $子ノード#動かない例:二重の関数 function elementMethod( $elem, $method ){ invokeComMethod $elem $method }function invokeComMethod{ $obj =$args[0] $methodName =$args[1] $others =if ( ($len=$args.length) -gt 2 ){ $args[2..($len-1)] } else { $null } return [System.__ComObject].InvokeMember( $methodName, [System.Reflection.BindingFlags]::InvokeMethod, $null, $obj, $others )}#########################以下Dom操作# CSSセレクタでノードを得る(複数ノード)function querySelectorAll($node, [string]$selector){ #どういうわけかinvokeComMethodで呼ぶとIEが落ちる $nodes = [System.__ComObject].InvokeMember("querySelectorAll", [System.Reflection.BindingFlags]::InvokeMethod, $null, $node, $selector) #$nodes =invokeComMethod $node "querySelectorAll" $selector #どういうわけか配列のままリターンするとIEが落ちる #return $nodes ではダメ $result = New-Object System.Collections.Generic.List[System.__ComObject] for ($i = 0; $i -lt $nodes.Length; $i++) { $result.Add([System.__ComObject].InvokeMember("item", [System.Reflection.BindingFlags]::InvokeMethod, $null, $nodes, $i)) } $result}# CSSセレクタでノードを得るfunction querySelector($node, [string]$selector){ return invokeComMethod $node "querySelector" $selector}function GetElementById( $node, [string]$ID ){ return invokeComMethod $node "getElementById" $ID}#どういうわけかこちらは配列のままでOK。むしろ、queryselectorAllと同様にすると動かない。以下同様function GetElementsByName( $node, [string]$NAME ){ return invokeComMethod $node "getElementsByName" $NAME}function GetElementsByTagName( $node, [string]$TagName ){ return invokeComMethod $node "getElementsByTagName" $TagName}function GetElementsByClassName( $node, [string]$ClassName ){ return invokeComMethod $node "getElementsByClassName" $ClassName}#########################テストプログラムif ( $myInvocation.InvocationName -like "*\ie.ps1" ){ # (1)Googleの検索結果一覧を取得してみる $ie =OpenUrl "https://www.google.co.jp/search?q=test" WaitIE $ie WaitForElement $ie "h3.r a" "LOADED" $links = querySelectorAll $ie.Document "h3.r a" $links | % { $_.innerText } "-----------------------" # (2)別のキーワードで検索してみる $text = querySelector $ie.Document "#lst-ib" $text.Value = "google" $btn = querySelector $ie.Document "#_fZl" $btn.click() WaitIE $ie "-----------------------" $keyword =getElementById $ie.Document "lst-ib" $keyword.Value "-----------------------" #dom操作を試みる $ie =OpenUrl "about:blank" WaitIE $ie $doc=$ie.document $body=getElementsByTagName $doc "body" #HTMLを直接書き込む $body.innerHTML=@"<div><input type="radio" name="rd" value="1">a<input type="radio" name="rd" value="2">b<input type="radio" name="rd" value="3">c</div>"@ #Dom操作でラジオボタンを追加 $radio_div =(getElementsByName $doc "rd")[0].parentNode $radio_d =invokeComMethod $doc "createElement" "input" invokeComMethod $radio_d "setAttribute" "type" "radio"|out-null invokeComMethod $radio_d "setAttribute" "name" "rd"|out-null invokeComMethod $radio_div "appendChild" $radio_d|out-null $radio_txt_d=invokeComMethod $doc "createTextNode" "d" invokeComMethod $radio_div "appendChild" $radio_txt_d|out-null $ie.Visible = $true IeActivate $ie sleep 2 #Dom操作でラジオボタンをチェック $radios =getElementsByName $doc "rd" $radio=$radios[1] invokeComMethod $radio "setAttribute" "checked"|out-null sleep 4 $ie.Visible = $false #Windowを消してアラート表示 windowMethod $ie "alert" "`"aaa`"" #Windowプロパティ取得 windowProperty $ie "location.Href" #Windowプロパティ代入 windowProperty $ie "location.Href" "https://www.infoseek.co.jp/" WaitIE $ie $ie.Visible = $true IeActivate $ie}