PowerShellでWebにアクセスしてみよう!
今回はPowerShellを使ってWeb(インターネット)にアクセスする方法を紹介します。
PowerShellは.NET Framework(.NET)をベースにして作られているので、.NETに用意されている豊富なライブラリをそのまま使用することができます。
Webへのアクセスについても.NETのライブラリを使うことになります。基本的にはC#やVB.NETなどの.NET対応言語と同じやり方になります。
WebへのアクセスにはSystem.Net.WebClientクラスかSystem.Net.HttpWebRequestクラスのどちらかを使って行います。
前者は簡単に使えますが細かい制御が出来ません、後者は少々手数が増えますが細かい制御が出来ます。今回はSystem.Net.HttpWebRequestクラスを使用します。
PowerShellからGoogle検索
Webと言えば「Google」と言えるほどの代名詞的な存在のGoogle検索をPowerShellから呼び出してみましょう。
今回作るのは引数として渡した文字列でGoogle検索を行い、その検索結果をコンソールに出力するPowerShellスクリプトです。
PowerShellを起動して、カレントディレクトリに「Find-Google.ps1」というファイルを作り、適当なテキストエディタ(秀丸エディタなど)で開いて下さい。
PS > ni -File Find-Google.ps1 PS > hidemaru Find-Google.ps1
まずは引数の定義を行います。文字列型で「word」という引数を定義しています。
param([string]$word)
次にこの引数に日本語が渡された場合に備えて(URLの一部として渡すため)、ASCIIコードに変換しておきます。
ASCIIコードに変換するには、System.Web.HttpUtilityクラスのUrlEncodeメソッドを使います。
このクラスはSystem.Webアセンブリに定義されているので、System.ReflectionクラスのLoadWithPartialNameメソッドを使ってアセンブリを読み込んでおきます。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word)
次に実際にGoogleにアクセスするためのコードを記述します。
やることはGoogleに検索のクエリを投げて(HTTPリクエスト) 、その結果を文字列として取得する(HTTPレスポンス)だけです。
Googleに投げる検索クエリのフォーマットは以下のようになります。
- http://www.google.co.jp/search?hl=ja&q=検索文字列
検索文字列の部分を引数で渡された文字列に置換します。
では、やってみましょう。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close()
System.Net.HttpWebRequestクラスのCreateメソッドを使って、引数で渡したURLにHTTPリクエストを送る、このクラスのインスタンスを生成します。
Methodプロパティに「GET」を設定することで、GETメソッドでHTTPリクエストを行います。
UserAgentプロパティには適当なユーザーエージェントを設定しておきます。ユーザーエージェントについては以下のURLを参考にして下さい。
ここまででHTTPリクエストの準備が整いました。後はGetResponseメソッドで実際にリクエストを行い、HTTPレスポンスを取得します。
GetResponseメソッドの返り値として、System.Net.HttpWebResponseクラスのインスタンスが返ってきますので、GetResponseStreamメソッドでHTTPレスポンスの出力ストリームを取得します。
後はこの出力ストリームをSystem.IO.StreamReaderクラスを使って読み出すだけです。出力ストリームとHTTPレスポンスを閉じるの(Closeメソッドの呼び出し)を忘れないで下さい。
ここで「content」という変数の中には、Google検索を使って検索した結果のHTMLが文字列として格納されています。
どのようなHTMLかは以下のURLにアクセスして、ソースを見てみて下さい。
このHTMLの中から検索結果の以下の情報を抽出して、PowerShellのコンソールに出力するわけです。
- タイトル
- URL
しかし、見てわかるとおりこのHTMLはかなり汚いです。タグが閉じられていなかったり、属性の値が引用符で囲まれていなかったりしています。
ある程度ならば少し変更してXHTMLにしてから、System.Xml.XmlDocumentクラスやSystem.Xml.Linq.XDocumentクラスなどを使って簡単に目的の情報を取得するのですが、このHTMLをちゃんとしたXHTMLに変換するのはかなり大変そうです。
なので、正規表現を使ってテキストレベルで情報を抽出します。ちなみに、こういった手法を使ってWebサイトの一部の情報だけを抜き出すことを「スクレイピング」と呼びます。
まずは正規表現で探しやすいように改行を削除しておきます。あと、邪魔なのでscript要素とstyle要素も削除しておきます(これは個人的な好みです)。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close() # 改行を削除 $content = $content -replace "`n", "" # スクリプトとスタイルを削除 $content = $content -replace "<(script|style)>.*?", ""
検索結果は一件ごとに「<li class=g>」というタグの中に入っていますが、なんとこのタグは閉じられていません。仕方が無いので、「</div>」か「</table>」というタグで閉じると仮定して正規表現で取りに行きます。
条件的には「<li class=g>(?<line>.*?)</(div|table)>」という感じになります。
これをSystem.Text.RegularExpressions.Regexクラスを使って行います(PowerShellには標準で-matchという正規表現用の演算子が用意されていますが、これは一件しか取得できないので使いません)。
param([string]$word) [void]([Reflection.Assembly]::LoadWithPartialName("System.Web")) $word = [Web.HttpUtility]::UrlEncode($word) $webReq = [Net.HttpWebRequest]::Create("http://www.google.co.jp/search?hl=ja&q=$word") $webReq.Method = "GET" $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)" $webRes = $webReq.GetResponse() $sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $webRes.ContentEncoding) $content = $sr.ReadToEnd() $sr.Close() $webRes.Close() # 改行を削除 $content = $content -replace "`n", "" # スクリプトとスタイルを削除 $content = $content -replace "<(script|style)>.*?</(script|style)>", "" $ptrn = New-Object Text.RegularExpressions.Regex("<li class=g>(?<line>.*?)</(div|table)>") $m = $ptrn.Matches($content) $m | % { if($_.Value -match '<h3 class=r><a href=\"(?<url>.*?)\" .*?>(?<title>.*?)</a>') { $obj = New-Object PSObject # タイトル $obj | Add-Member NoteProperty Title ($matches.title -replace "</*\w*>", "") # URL $obj | Add-Member NoteProperty Url $matches.url $obj } }
後は正規表現の条件にマッチした結果をForEach-Objectコマンドレット(%エイリアス)を使って、要素の数だけぐるぐる回します。
その中では「<h3 class=r><a href=\"(?<url>.*?)\" .*?>(?<title>.*?)</a>」という条件でタイトルとURLを取得して、それらをPSObjectクラスのNotePropertyとして追加して、コンソールに出力します。
このスクリプトを実行してみると、以下のように表示されます。
PS > Find-Google powershell Title Url ----- --- Windows PowerShell でのスクリプティング http://www.microsoft.com/japan/technet/scriptcenter/hubs... Windows PowerShell - Wikipedia http://ja.wikipedia.org/wiki/Windows_PowerShell オブジェクト指向なコマンド環境「Powershell」を試してみた... http://d.hatena.ne.jp/nitoyon/20080806/powershell_tutorial 次世代Windowsシェル「Windows PowerShell」を試す(前編) ... http://www.atmarkit.co.jp/fdotnet/special/powershell01/p... Windows PowerShell徹底解説:ITpro http://itpro.nikkeibp.co.jp/article/COLUMN/20061106/252598/ Windows PowerShell 入門(1)−基本操作編:CodeZine http://codezine.jp/article/detail/2067?p=1 PowerShell FAQ http://newpops.wankuma.com/ PowerShell Scripting http://www.roy.hi-ho.ne.jp/mutaguchi/powershell/
これは完全に動作するわけではないですが、Webにアクセスする方法の初歩とのサンプルとしてはいいのではないでしょうか。
まだまだ足らない機能がありますが、この先は皆さんの手で作ってみて下さい。