今回はWebブラウザのヘッドレスモードという機能を使ってWebページのコンテンツを取得する方法を試し、これをPowerShellスクリプトに取り込む実装を進める。これでPowerShellスクリプトで取得できるWebページの数が増えることになる。

前回までに作成したスクリプト

前回までに作成した、Webページのコンテンツを自動入力するPowerShellスクリプト「netcat.ps1」は次の通りだ。最近のWindowsには、デフォルトでデプロイされている「curl.exe」を使って指定されたURLのコンテンツをダウンロードする処理を行っている。

#!/usr/bin/env pwsh

#========================================================================
# URLで指定されたリソースを取得
#========================================================================

#========================================================================
# 引数を処理
#   -URL url        WebリソースのURL
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$URL = "",
    [String]$Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
)

#========================================================================
# Webリソース取得に利用するアプリケーション
#========================================================================
$curl='C:\Windows\System32\curl.exe'

#========================================================================
# curl を使って取得する
#========================================================================
& $curl --location                      `
    -A $Agent                       `
    -get $URL

しかし、例えば「https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun」のように、ダウンロードした後にJavaScriptを実行して本体となるコンテンツを取得するタイプのWebページは、この方法では取得できない。クライアント側でJavaScriptを実行しないと、欲しいデータの自動取得が完成しないのだ。

これを実現する方法はいくつかあるが、最も強力な方法がWebブラウザをそのまま使う方法だ。今回はその使い方を探っていき、netcat.ps1へ機能を取り込んでいく。

Microsoft Edgeのヘッドレスモード

Windows 10やWindows 11には「Microsoft Edge」がデフォルトブラウザとして用意されている。

現在のMicrosoft EdgeはGoogle Chromeの基盤技術であるChromiumを使っている。これはつまり、Google Chrome(Chromium)に実装されている「ヘッドレスモード」が、Microsoft Edgeでも使えることを意味している。デフォルトでインストールされているWebブラウザで使えるのだから、これを使うことにしよう。

まず、PowerShellからMicrosoft Edgeを実行したいので、このプログラムがどこにデプロイされているのか調べる。Microsoft Edgeの起動は「msedge.exe」という実行ファイルから行うので、これがどこにあるのかを調べる。次のようにファイルエクスプローラで調べればよいだろう。

  • ファイルエクスプローラでmsedge.exeのパスを調べる

    ファイルエクスプローラでmsedge.exeのパスを調べる

  • msedge.exeのパスはC:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

    msedge.exeのパスはC:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

ファイルエクスプローラで調べると、「msedge.exe」へのパスは「C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe」であることがわかる。

試しにこれをPowerShellプロンプトから実行してみよう(パスが空白を含んでいる場合には、以下のように「&」を指定して実行する必要がある)。

& 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'

すると、Microsoft Edgeが起動してくれば適切なパスが得られていることになる。

  • msedge.exeで起動してきたMicrosoft Edge

    msedge.exeで起動してきたMicrosoft Edge

Microsoft Edgeが起動することを確認したら、今度は次のように引数としてURLを指定してみよう。前回までのPowerShellスクリプトでは取得することができなかった「https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun」を指定して実行してみる。

& 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe' https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun

次のように指定したURLを開いた状態でMicrosoft Edgeが起動してくれば成功だ。

  • URLを開いた状態でMicrosoft Edgeが起動

    URLを開いた状態でMicrosoft Edgeが起動

後は、これを画面ではなく直接ファイルに出力すればよい。msedge.exeはオプションを指定することでヘッドレスモードで動作させることができる。これにほかのオプションを組み合わせると、画面ではなくターミナルへのコンテンツ(HTML)出力が可能だ。ここでは「--headless --dump-dom --enable-logging」というオプションを指定すればよいことだけ説明しておこう。

つまり、次のように指定して実行すればよい。

& 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe' --headless --dump-dom --enable-logging https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun

実行結果は次の通りだ。

PS C:\Users\daichi> & 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe' --headless --dump-dom --enable-logging https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun
PS C:\Users\daichi> <!DOCTYPE html>
<html lang="en" dir="ltr"><head><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><meta charset="utf-8"><meta http-equiv="Pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"><link rel="icon" href="/update-guide/favicon/favicon.svg"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#0078d4"><meta name="color-scheme" content="dark light"><meta name="description" content="<h2>今月の更新プログラム</h2>
今回のリリースは、次の製品、機能、およびロールのセキュリティ更新プログラムで構成されています。
<ul>
<li>.NET と Visual Studio</li>
<li>Azure OMI</li>
<li>Azure リアルタイム オペレーティング システム</li>
<li>Azure Service Fabric コンテナー</li>
<li>Intel</li>
<li>Microsoft Edge (Chromium ベース)</li>
<li>Microsoft Office</li>
<li>Microsoft Office Excel</li>
<li>Microsoft Office SharePoint</li>
<li>Microsoft Windows ALPC</li>
<li>Microsoft Windows Codecs Library</li>
<li>リモート ボリューム シャドウ コピー サービス (RVSS)</li>
<li>ロール: Windows Hyper-V</li>
<li>SQL Server</li>
<li>Windows Ancillary Function Driver for WinSock</li>
<li>Windows App Store</li>
<li>Windows Autopilot</li>
<li>Windows Container Isolation FS Filter ドライバー</li>
<li>Windows Container Manager サービス</li>
<li>Windows Defender</li>
<li>Windows Encrypting File System (EFS)</li>

...略...

PS C:\Users\daichi>

curl.exeを使ったときには取得できなかった「https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun」の中身がテキストで取得できていることがわかる。

ヘッドレスモードというのは、名前の通りヘッドのない状態、つまりウインドウを起動しない状態で動作させることを意味している。コンテンツだけ得られればウインドウを表示する必要はないため、ヘッドレスモードで動作することが好ましいのだ。

このヘッドレスモードは、WebアプリケーションやWebサービスの開発やデバッグにおいてよく使われる機能でもある。処理を効率良く行うために欠かせない機能であり、開発者にはなじみの深い機能なのだ。

スクリプトにMicrosoft Edgeのヘッドレスモードを取り込む

今回の成果をnetcat.ps1へ反映させると次のようになる。

#!/usr/bin/env pwsh

#========================================================================
# URLで指定されたリソースを取得
#========================================================================

#========================================================================
# 引数を処理
#   -URL url        WebリソースのURL
#   -Agent agent    リクエストで使うエージェントを指定
#========================================================================
Param(
    [Parameter(Mandatory=$true)][String]$URL = "",
    [String]$Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
)

#========================================================================
# Webリソース取得に利用するアプリケーション
#========================================================================
$msedge='C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
$curl='C:\Windows\System32\curl.exe'

#========================================================================
# どの方法でWebリソースを取得するかを判断
#========================================================================
if (Test-Path $msedge) {

    #================================================================
    # Microsoft Edgeを使って取得
    #================================================================
    $method='msedge'
}
elseif (Test-Path $curl) {

    #================================================================
    # curl を使って取得
    #================================================================
    $method='curl'
}
else {
    $method='none'
}

#========================================================================
# Webリソースを取得
#========================================================================
switch ($method)
{
    #================================================================
    # Microsoft Edgeを使って取得
    #================================================================
    'msedge'
    {
        $o1='--headless'
        $o2='--dump-dom'
        $o3='--enable-logging'
        Start-Process   -FilePath $msedge           `
                -ArgumentList $o1,$o2,$o3,$URL      `
                -Wait
    }

    #================================================================
    # curl を使って取得
    #================================================================
    'curl'
    {
        & $curl     --location              `
                -A $Agent               `
                -get $URL
    }
}

まず今回は簡単に、Microsoft EdgeがあればMicrosoft Edgeを、ない場合にはcurl.exeを探しcurl.exeがあればcurl.exeを使うようにPowerShellスクリプトを書き換えている。

PowerShellプロンプトでインタラクティブに実行したときと異なるのは、msedge.exeを実行するにあたって「&」ではなく「Start-Process」コマンドレットを使っている点にある。「&」で実行するとプロセスを起動してすぐにPowerShellスクリプトに処理が戻ってしまうので、「Start-Process」に「-Wait」を指定して、処理が完結するまで待っている。こうしておかないとコマンドとしては使いにくいのだ。

では、今回新しくしたnetcat.ps1を実行してみよう。次のように前回までは取得できなかったWebページの内容が取得できるようになっていることがわかる。

PS C:\Users\daichi> netcat.ps1 https://msrc.microsoft.com/update-guide/releaseNote/2022-Jun
<!DOCTYPE html>
<html lang="en" dir="ltr"><head><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><style data-merge-styles="true"></style><meta charset="utf-8"><meta http-equiv="Pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"><link rel="icon" href="/update-guide/favicon/favicon.svg"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#0078d4"><meta name="color-scheme" content="dark light"><meta name="description" content="<h2>今月の更新プログラム</h2>
今回のリリースは、次の製品、機能、およびロールのセキュリティ更新プログラムで構成されています。
<ul>
<li>.NET と Visual Studio</li>
<li>Azure OMI</li>
<li>Azure リアルタイム オペレーティング システム</li>
<li>Azure Service Fabric コンテナー</li>
<li>Intel</li>
<li>Microsoft Edge (Chromium ベース)</li>
<li>Microsoft Office</li>
<li>Microsoft Office Excel</li>
<li>Microsoft Office SharePoint</li>
<li>Microsoft Windows ALPC</li>
<li>Microsoft Windows Codecs Library</li>
<li>リモート ボリューム シャドウ コピー サービス (RVSS)</li>
<li>ロール: Windows Hyper-V</li>
<li>SQL Server</li>
<li>Windows Ancillary Function Driver for WinSock</li>
<li>Windows App Store</li>
<li>Windows Autopilot</li>
<li>Windows Container Isolation FS Filter ドライバー</li>
<li>Windows Container Manager サービス</li>
<li>Windows Defender</li>
<li>Windows Encrypting File System (EFS)</li>

...略...

</tbody>
</table>
</div><p class="css-184">リリース日: 2022/07/15</p></div></div></div></div></div></div></div></div></div></div><script nomodule="">String.prototype.endsWith||(String.prototype.endsWith=function(t,n){return(void 0===n||n>this.length)&&(n=this.length),this.substring(n-t.length,n)===t})</script><script async="" src="https://wcpstatic.microsoft.com/mscc/lib/v2/wcp-consent.js"></script><script src="https://amcdn.msftauth.net/me?partner=MSMSRC"></script><iframe sandbox="allow-scripts allow-same-origin allow-forms" style="visibility: hidden; position: absolute; height: 0px; width: 0px; border: 0px;"></iframe></body></html>
PS C:\Users\daichi>

mail.ps1を作成したときも説明したが、このようにして作っていったスクリプトで大切なのはインタフェースであり、PowerShellスクリプトの中の処理がどうなっているかはさほど問題ではないのだ。その時々に適切な技術を使って実装し、スクリプトのインタフェースだけ変えなければよい。

現段階のnetcat.ps1は、まだまだ荒削りだ。ここまで改良してきたが、まだ取得できないWebページやコンテンツがある。次回以降、さらに改良を加えながら、実用的なPowerShellスクリプトへ仕上げていく。