前回までに作成したPowerShellスクリプトは、実はPDFやCSVなど、HTML以外のコンテンツに対して使おうとするとうまく機能しない。今回はこの部分を改善する。
HTMLに対してはうまく機能する
前回、Microsoft Edgeのヘッドレスモードを使うようにしたことで、JavaScriptを使ってコンテンツを取得して表示しているWebページに対してもコンテンツの取得が可能になった。これで取得できるWebページの幅は大きく広がった。前回までの成果物「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'
$o4='--user-agent="$agent"'
$tmpf=New-TemporaryFile
Start-Process -FilePath $msedge `
-RedirectStandardOutput $tmpf `
-ArgumentList $o1,$o2,$o3,$o4,$URL `
-Wait
Get-Content $tmpf
Remove-Item $tmpf
}
#================================================================
# curl を使って取得
#================================================================
'curl'
{
& $curl --location `
-A $Agent `
-get $URL
}
}
この方法は強力ではあるのだが、実はHTML以外のコンテンツに対してはうまく機能しない。Webブラウザのヘッドレスモードで取得できるコンテンツは基本的にHTML (text/html)であって、それ以外を取得しようとすると思っているように動いてくれないのだ。
しかし、実際にはWebページ経由でPDFを取得するとか、CSVデータやTSVデータを取得するといった操作は結構行うため、それができないとなると自動化の足かせとなる。特に、CSVやTSVといったテキストベースのデータファイルを取得できないのはとても困る。今回はこの部分を改善する。
今回のポイントはcurl
これまでの取り組みで、curlからMicrosoft Edgeのヘッドレスモードへシフトさせたわけだが、今回は逆にcurlへと回帰する。
curlにはJavaScript実行エンジンは搭載されていないため、JavaScriptで動作するタイプのWebサイトに対してはこちらが期待しているような動作は期待できない。しかし、それ以外の面ではcurlは強力この上ない。このあたりを整理すると、次のようにプログラムの切り分けを行うことが、Webページ取得PowerShellスクリプトに向いているということになる。
- 対象コンテンツの種類を判断 - (A)
- 対象コンテンツがtext/htmlだった場合はMicrosoft Edgeのヘッドレスモードでコンテンツを取得する
- 対象コンテンツがtext/html以外あった場合はcurlでコンテンツを取得する
シンプルであり、かつ、強力な方法だ。
(A)は、例えば「URLの最後が.htmlや.htmだった」とか、「.csvや.pdfだったら」といった条件で判断するのが簡単だ。だが、URLで指定されているパスに拡張子がない場合も多く、これだけを判断基準とするのはちょっとばかり無理がある。
今回はまず基本的な方法として、サーバと通信してコンテンツの種類情報を取得し、処理を切り分ける方法を実装する。
先に答えを書いておくと、これはcurlを使うことで実現可能だ。細かいオプションの話は省くが、curlに「-Ss -I」を指定してURLを渡すと、そのレスポンスヘッダを得ることができる。レスポンスヘッダにコンテンツの種類が書いてあるので、そこから判断するというわけだ。
次のスクリーンショットは「Operating System Market Share Worldwide | Statcounter Global Stats」に掲載されているCSVデータファイルのURLを「curl.exe -Ss -I」で調べたものだ。次のようになる。
出力されたヘッダに「content-type: 」という文字列から始まる行がある。これがコンテンツの種類だ。コンテンツの種類は「application/octet-stream」となっており、「text/html」ではない。このデータはWebブラウザのヘッドレスモードではなく、curlで取得すべきデータだということになる。