すごいPowerShell
PowerShellとは
2023年11月、PowerShellスクリプトでWEBスクレイピングをやってみました。PowerShellは、ちょっと癖がありますけどすごく強力です。
PowerShellは、Microsoftが開発したスクリプト言語です。とほほのPowerShell入門サイトに解説があります。
PowerShellでは、利用可能なコマンドのことをコマンドレット(cmdlet)と呼びます。コマンドレットは、「動詞-名詞」という命名規則になっています。例えば、「Get-Item」というように「どうする-何を」という形式になっているため慣れれば理解しやすいです。
PowerShellのバージョン確認
Windowsのスタートメニューから統合開発環境「PowerShell ISE」を起動します。
コマンドラインから「$PSVersionTable」と入力すると、バージョンテーブルを表示します。
「$PSVersionTable.PSVersion」と入力すると、バージョン番号のみを表示します。
PS E:\> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.3693
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.3693
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
PS E:\> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 19041 3693
PS E:\>
統合開発環境「PowerShell ISE」
統合開発環境「PowerShell ISE」(Windows PowerShell Integrated Scripting Environment )は、Windows PowerShellのスクリプト作成から実行、デバッグを行うためのツールです。
左下段に実行ウィンドウ、右側にコマンドウィンドウ、左上段にスクリプトウィンドウがあります。
- 実行ウィンドウは、コマンドレットを入力して実行して実行結果を表示します。
- コマンドウィンドウは、各コマンドレットのヘルプや引数入力フォームを表示することができます。
- スクリプトウィンドウは、スクリプトを入力します。上部の実行ボタン(▶)を押すことで実行できます。
PowerShellの簡単なスクリプト
WEBスクレイピングとは
WEBスクレイピングとは、WEBサイトから特定の情報を自動的に抽出することです。
PowerShellでは、Invoke-WebRequestコマンドレットで簡単にWEBページにアクセスしてHTMLをパースすることができます。
プログラム仕様
簡単な例として 「bit目次の索引」からTITLEタグを抽出してみます。
このサイトには、3つの索引(題名、著者、単語)があります。
それぞれのHTMLは、”indexttl.html”、”indexaut.html”、”indexkey.html”です。
このサイトから、TITLEタグを抽出します。
PowerShellスクリプトの実行結果
スクリプト名を「get_title.ps1」とします。
最初に実行結果を示します。
PS E:\> E:\ps\get_title.ps1
http://my-web-site.iobb.net/~yuki/bit/
indexttl.html =====> 索引(題名) | bit目次
indexaut.html =====> 索引(著者) | bit目次
indexkey.html =====> 索引(単語) | bit目次
PS E:\>
PowerShellスクリプト
このPowerShellスクリプトを下記に示します。
###=== WEB タイトル取得 ===
function get_title($url) {
$title_text = "???"
#== Get HTML
try {
$response = Invoke-WebRequest -Uri $url -TimeoutSec 5
} catch {
Write-Host "??? Error ",$url
return($title_text)
}
$oHtml = $response.ParsedHtml
# TITLE タグ
$elements = $oHtml.getElementsByTagName("title")
foreach($elm in $elements) {
$title_text = $elm.outerText
$title_html = $elm.outerHTML
Write-Debug $title_html
}
return($title_text)
}
### Main
#$debugPreference = "Continue"
#
$url_ary = @("indexttl.html" , "indexaut.html" , "indexkey.html")
$url_base = "http://my-web-site.iobb.net/~yuki/bit/"
Write-Host $url_base
foreach ($url in $url_ary) {
$url_adr = $url_base + $url
$title = get_title $url_adr
Write-Host " " $url " =====> " $title
}
関数 get_title($url)
function get_title($url) {
}
関数 get_title($url)は、引数で指定したWEBサイトにアクセスしてTITLEタグの内容を返します。
try {
$response = Invoke-WebRequest -Uri $url -TimeoutSec 5
} catch {
Write-Host "??? Error ",$url
return($title_text)
}
try { 処理 } catch { エラー処理 } は、処理中にエラーが発生したときにエラーを捕らえてエラー処理を行います。
105行目の「Invoke-WebRequest -Uri $url」で指定したWEBページにアクセスして、WEB情報を$responseに格納します。
$oHtml = $response.ParsedHtml
WEBページのHTML情報を取得して$oHtmlに格納します。
$elements = $oHtml.getElementsByTagName("title")
HTML情報からTITLEタグを検索して$elementsに格納します。
TITLEタグなので基本的には1個だけです。しかし、他のタグ(例えば、DIVタグ)の場合は、複数個検出する可能性があります。
foreach($elm in $elements) {
$title_text = $elm.outerText
$title_html = $elm.outerHTML
Write-Debug $title_html
}
foreach文で$elementsの要素数だけ繰り返します。
ひとつの要素$elmについて、
- タグ内のテキスト(outerText)を$title_textに格納
- HTMLテキスト(outerHTML)を$title_htmlに格納
- $title_htmlをデバッグ用に出力
return($title_text)
TITLEタグ内のテキスト$title_textを戻り値とします。
メイン処理
#$debugPreference = "Continue"
変数$debugPreferenceは、デバッグ情報の制御です。コメントアウトしているので、デバッグ情報は無効です。コメントを外すと、Write-Debugでデバッグ情報を表示します。
$url_ary = @("indexttl.html" , "indexaut.html" , "indexkey.html")
配列$url_aryに3つの文字列を格納します。@(○ , ○ , ○)は、配列を作成します。
$url_base = "http://my-web-site.iobb.net/~yuki/bit/"
変数$url_baseにWEBページのURLベースを格納します。
Write-Host $url_base
変数$url_baseを表示します。
foreach ($url in $url_ary) {
$url_adr = $url_base + $url
$title = get_title $url_adr
Write-Host " " $url " =====> " $title
}
foreach文で$url_aryの要素数だけ繰り返します。
ひとつの要素$urlについて、
- $url_adrに$url_baseと$urlを連結した文字列を格納
- 関数get_title を引数$url_adrで呼び出し、戻り値を$titleに格納します。
- Write-Host文で$urlと$titleを表示します。
ここで関数呼び出しに注意してください。引数を括弧で囲む必要はありません。また、もし引数が複数個あればカンマで区切らず並べるだけです。カンマで区切ると配列を作成することになります。
デバッグ情報について
変数$debugPreferenceは、下記に示すような値を設定します。
設定値 | 動作 |
---|---|
Continue | デバッグメッセージを表示して実行を継続する |
SilentlyContinue | デバッグメッセージは表示されず、中断することなく実行する |
Inquire | デバッグメッセージを表示し、続行するかどうか確認する |
Stop | デバッグメッセージを表示し、実行を停止する |
変数$debugPreferenceのコメントアウトを外すと、Continueとなりますので、下記のような実行結果となります。
PS E:\> E:\ps\get_title.ps1
http://my-web-site.iobb.net/~yuki/bit/
デバッグ: <TITLE>索引(題名) | bit目次</TITLE>
indexttl.html =====> 索引(題名) | bit目次
デバッグ: <TITLE>索引(著者) | bit目次</TITLE>
indexaut.html =====> 索引(著者) | bit目次
デバッグ: <TITLE>索引(単語) | bit目次</TITLE>
indexkey.html =====> 索引(単語) | bit目次
PS E:\>
Write-Debug文でデバッグ情報を表示できました。
強力なPowerShell
PowerShellスクリプトは、とても強力です。
他のプログラミング言語を知っていれば、比較的容易にプログラミング可能だと思います。