PowershellによるIEWebスクレイピング
Internet ExplorerをPowershellでWebスクレイピングする方法
IEでしか動かないページがまだ有るという悲しい状況でWebスクレイピングする方法を探してみました。
HTML要素の取得
一番簡単なhttpリスクエストに対する応答(Response)を取得するだけならInvoke-WebRequestで実行できます。
1 2 |
$URL ="http://server.contoso.local/personal" $Response = Invoke-WebRequest -Uri $URL |
認証が必要ならCredentialsオプション、プロキシが必要ならProxyオプションを使いましょう。
httpsを要求するサイトも増えてきました。そういったサイトの場合、Invoke-WebRequestが失敗します。
例:
1 2 |
$URL = "https://www.yahoo.co.jp/" $Response = Invoke-WebRequest -Uri $URL |
上記を実行すると、下記のエラーが返ってきます。
1 2 3 4 5 6 7 8 |
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の設定を確認してみましょう。
1 2 |
PS C:\> [Net.ServicePointManager]::SecurityProtocol Ssl3, Tls |
SSL3.0,TLS1.0のようです。TLS1.0は脆弱性が報告されていて、TLS1.2以上を要求されます。
.Netでの記法を確認してみましょう。
1 2 3 4 5 6 7 |
PS C:\> [enum]::GetNames([Net.SecurityProtocolType]) SystemDefault Ssl3 Tls Tls11 Tls12 Tls13 |
Tls12で良いようなので、これを追加しましょう。
1 2 3 4 |
PS C:\> [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12 PS C:\> [Net.ServicePointManager]::SecurityProtocol Ssl3, Tls, Tls12 |
HtmlWebResponseObjectというオブジェクトに格納されます。オブジェクトのMemberを見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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")
表示されている文字列を取り出すのであれば下記で出来ます
1 |
$Text = $Response.ParsedHtml.body.innerText |
これ、結果にSystem.__ComObjectが入ってくるんですが、内部的にIEをCOM呼び出しで実装しているようで、取り出してみないとどう格納されているのかわからないところがあります。
Comオブジェクトを使ったIEの操作
Invoke-WebRequest便利なんですが、返ってくるのは静的HTMLです。動的に表示する要素はxxxx.jsなどjava scriptへの参照が表示されるだけです。これで済むのであればIEじゃなくてもどのブラウザでも表示できるので、「IEじゃないと動かない」ってことは無いんじゃないですかね?
そういった場合、ブラウザに動的ページを表示させて、その結果を取得するというのは可能というのは過去の経験から分かっています。ということで、IEを外部から操作してみます。
IEを外部操作しようとすると、今のところComオブジェクトになるようです。
1 2 3 4 5 6 7 8 9 10 11 |
$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して変数を初期化しておきましょう。
1 2 |
$IE.Quit() $IE=$Nothing |