PowershellによるIEWebスクレイピング

Internet ExplorerをPowershellでWebスクレイピングする方法

IEでしか動かないページがまだ有るという悲しい状況でWebスクレイピングする方法を探してみました。

HTML要素の取得

一番簡単なhttpリスクエストに対する応答(Response)を取得するだけならInvoke-WebRequestで実行できます。

$URL ="http://server.contoso.local/personal"
$Response = Invoke-WebRequest -Uri $URL

認証が必要ならCredentialsオプション、プロキシが必要ならProxyオプションを使いましょう。

httpsを要求するサイトも増えてきました。そういったサイトの場合、Invoke-WebRequestが失敗します。
例:

$URL = "https://www.yahoo.co.jp/"
$Response = Invoke-WebRequest -Uri $URL

上記を実行すると、下記のエラーが返ってきます。

PS C:\> $Response = Invoke-WebRequest -Uri $URL
Invoke-WebRequest : 要求は中止されました: SSL/TLS のセキュリティで保護されているチャネルを作成できませんでした
発生場所 行:1 文字:13
+ $Response = Invoke-WebRequest -Uri $URL
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebE 
   xception
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Powershellで使われているSSL/TLSの設定を確認してみましょう。

PS C:\> [Net.ServicePointManager]::SecurityProtocol
Ssl3, Tls

SSL3.0,TLS1.0のようです。TLS1.0は脆弱性が報告されていて、TLS1.2以上を要求されます。
.Netでの記法を確認してみましょう。

PS C:\> [enum]::GetNames([Net.SecurityProtocolType])
SystemDefault
Ssl3
Tls
Tls11
Tls12
Tls13

Tls12で良いようなので、これを追加しましょう。

PS C:\> [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12

PS C:\> [Net.ServicePointManager]::SecurityProtocol
Ssl3, Tls, Tls12

HtmlWebResponseObjectというオブジェクトに格納されます。オブジェクトのMemberを見てみましょう。

TypeName: Microsoft.PowerShell.Commands.HtmlWebResponseObject

Name              MemberType Definition                                                                 
----              ---------- ----------                                                                 
Dispose           Method     void Dispose(), void IDisposable.Dispose()                                 
Equals            Method     bool Equals(System.Object obj)                                             
GetHashCode       Method     int GetHashCode()                                                          
GetType           Method     type GetType()                                                             
ToString          Method     string ToString()                                                          
AllElements       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;}
BaseResponse      Property   System.Net.WebResponse BaseResponse {get;set;}                             
Content           Property   string Content {get;}                                                      
Forms             Property   Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;}            
Headers           Property   System.Collections.Generic.Dictionary[string,string] Headers {get;}        
Images            Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;}     
InputFields       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;}
Links             Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;}      
ParsedHtml        Property   mshtml.IHTMLDocument2 ParsedHtml {get;}                                    
RawContent        Property   string RawContent {get;set;}                                               
RawContentLength  Property   long RawContentLength {get;}                                               
RawContentStream  Property   System.IO.MemoryStream RawContentStream {get;}                             
Scripts           Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;}    
StatusCode        Property   int StatusCode {get;}                                                      
StatusDescription Property   string StatusDescription {get;}

返ってきたhtml応答をparse(解析)した結果が格納されます。ImageやLink,Scriptなんかは配列として格納されているのでこのまま参照できます。その他のhtml tagについてはPasedHTMLのgetElementsByTagNameメソッドで取り出す事ができます。
$Table=$Response.ParsedHtml.getElementsByTagName("table")

表示されている文字列を取り出すのであれば下記で出来ます

$Text = $Response.ParsedHtml.body.innerText

これ、結果にSystem.__ComObjectが入ってくるんですが、内部的にIEをCOM呼び出しで実装しているようで、取り出してみないとどう格納されているのかわからないところがあります。

Comオブジェクトを使ったIEの操作

Invoke-WebRequest便利なんですが、返ってくるのは静的HTMLです。動的に表示する要素はxxxx.jsなどjava scriptへの参照が表示されるだけです。これで済むのであればIEじゃなくてもどのブラウザでも表示できるので、「IEじゃないと動かない」ってことは無いんじゃないですかね?

そういった場合、ブラウザに動的ページを表示させて、その結果を取得するというのは可能というのは過去の経験から分かっています。ということで、IEを外部から操作してみます。

IEを外部操作しようとすると、今のところComオブジェクトになるようです。

$URL = "http://www.google.co.jp"

$IE = New-Object -ComObject InternetExplorer.Application

$IE.Visible = $true
$IE.Navigate($URL, 4)
while ($IE.busy -or $IE.readystate -ne 4)
    {Start-Sleep -Seconds 1}

$Doc = $IE.Document
$Text = $Doc.body.innerText

これはアプリケーション間の操作として作られていてVBAなどでオブジェクトを作って操作する方法が紹介されています。

InternetExplorer.Applicationで検索するとExcel VBAの書き方がたくさんヒットします。
Windows上のオブジェクトなので終わったらQuitして変数を初期化しておきましょう。

$IE.Quit()
$IE=$Nothing

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA


This site uses Akismet to reduce spam. Learn how your comment data is processed.