bit目次を検索するPowerShellスクリプト

bit目次の検索

bit目次の検索

PowerShellでbit目次を検索したい

2023年12月、PowerShellでbit目次を検索するスクリプトを作成しました。

先日、コンピュータ・サイエンス誌『bit』の総合目次を作成して便利に使っています。索引(単語)にテクニカルターム(キーワード)の索引があるのですが、ここにないキーワードで記事を探すのがちょっと面倒です。

コンピュータ・サイエンス誌『bit』の総合目次がとても便利

PowerShellはWEBスクレイピングが簡単にできるので、PowerShellスクリプトでbit総合目次を検索してみようと挑戦しました。

すごいPowerShell WEBスクレイピングが簡単!

bit総合目次を検索

bit総合目次は、1月号~12月号をひとつのHTML文書「bit_yyyy.html」として年ごとに目次を掲載しています。
その中は、下記のような構成となっています。

タグ 内容
<h2> YYYY-MM 形式で年月
<dt> タイトル
<dd> 内容など
bit目次検索の概要

bit目次検索の概要

HTML文書から<h2>タグ、<dt>タグ、<dd>タグを抽出したテキストからキーワードを検索すれば良いわけです。

「Invoke-WebRequest」で指定したWEBページにアクセスして、bit目次のHTMLを取得します。
そして、「getElementsByTagName()」で<h2>タグ、<dt>タグ、<dd>タグを抽出することを考えました。
しかし、個別に抽出するのでタグの対応関係がとれなくなってしまいます。

<h2>タグ、または、<dt>タグ、または、<dd>タグを一度に抽出することはできないのです。

bit目次検索 タグ抽出

bit目次検索 タグ抽出

どのような手法でこれを実現するか少し悩みました。
これは、いつか来た道だな。昔もこんなことを考えたことがあるなと思いブログを見直しました。

「JavascriptによるDOM操作で目次を生成する方法」で同じようなことをやっているではないですか。

JavascriptによるDOM操作で目次を生成する方法

クラス名で抽出する方法があるので、抽出したいタグに同じクラス名を追加すれば良いのです。
そして、getElementsByClassName()を使って対象タグを抽出します。

bit目次検索 クラス名抽出

bit目次検索 クラス名抽出

PowerShellスクリプト

bit目次を検索するPowerShellスクリプトは次のようになります。


#
### bit 総合目次 キーワード検索
#
#
function search_key($url ,$key) {
    #       == Get HTML
    try {
        $response = Invoke-WebRequest -Uri $url  -TimeoutSec 5
    } catch {
       Write-Host "Error ",$url
       return
    }
    $oHtml = $response.ParsedHtml
    set_class $oHtml "h2" "search" 
    set_class $oHtml "dt" "search" 
    set_class $oHtml "dd" "search" 
    #
    $cls_elms  = $oHtml.getElementsByClassName("search") 
    foreach($cls_elm in $cls_elms) {
        switch ($cls_elm.tagName) {
           ({$_ -eq "h2"}) { $yyyymm = $cls_elm.innerText.Substring(0,7) }
           ({$_ -eq "dt"}) { $dt_text = $cls_elm.innerText.Replace("`n", "") }
           ({$_ -eq "dd"}) { $dd_text = $cls_elm.innerText.Replace("`n", "") }
        }
        if ($cls_elm.tagName -eq "dd") {
            $chk = check_key $dt_text+" "+$dd_text $key
            if ($chk -gt 0) {
                Write-Host $yyyymm $dt_text $dd_text
            }
        }
    }
}
# $Elmの タグ$Tag に クラス$Class を追加
function set_class($Elm ,$Tag ,$Class) {
    $tag_elms = $Elm.getElementsByTagName($Tag)
    foreach($tag_elm in $tag_elms) {
       $oClass = $tag_elm.ClassName
       $tag_elm.ClassName = $Class+" "+$oClass
    }
}
# テキスト$textからキーワード$keyを探す 戻り値=1(有)/0(無)
function check_key($text ,$key) {
   $text = " "+$text+" "
   $rc_chk = 0
   if ($key -match '\d+') {
        if ($text -match "[^\d]$key[^\d]") {
            $rc_chk = 1
        }
   } else {
        if ($text -match "[^A-Z]$key[^A-Z]") {
            $rc_chk = 1
        }
   }
   return($rc_chk)
}
#
###  Main
#
# $debugPreference = "Continue"
#
$yyyy_s = 1974
$yyyy_e = 1992
$url_base = "http://my-web-site.iobb.net/~yuki/bit/"
#
Write-Host  "bit 総合目次 キーワード検索 Start!"
$key = Read-Host "Please input KEY word "   # キーワード入力
Write-Host "`r`n",$key
#
for ($yyyy = $yyyy_s; $yyyy -le $yyyy_e; $yyyy++) {
    $url = $url_base+"bit_"+$yyyy+".html"
    # Write-Debug $yyyy
    search_key $url $key
}

関数 search_key

関数 search_keyは、指定したWEBページをスクレイピングして検索キーワードに該当するbit誌の年月、タイトル、内容を表示します。

引数 説明
$url WEBページのURL
$key 検索キーワード
戻り値 無し
  • $urlで指定したWEBページにアクセスして、$oHtmlにHTML情報を読み込みます。
  • <h2> タグに class=”search” を追記します。
  • <dt> タグに class=”search” を追記します。
  • <dd> タグに class=”search” を追記します。
  • getElementsByClassName()でクラス名”search”を順次アクセスします。
  • タグ種別により、HTML情報を取得します。
    • <h2> タグなら、先頭7文字の年月を$yyyymmに格納
    • <dt> タグなら、タイトルを$dt_textに格納
    • <dd> タグなら、内容などを$dd_textに格納
  • <dd> タグを検出したとき、関数 check_keyで$dt_textと$dd_textに検索キーワードに該当するかチェックします。
    該当した場合、「$yyyymm $dt_text $dd_text」を出力します。

関数 set_class

関数 set_classは、HTMLページの指定したタグにクラス名を追記します。

引数 説明
$Elm HTMLページ
$Tag HTMLタグ
$Class 追記するクラス名
戻り値 無し

getElementsByTagName()で指定したタグを順次アクセスして、クラス名を追記します。

例えば、<h2>タグにクラス”search”を追記する場合は、「<h2>1974-01 (通巻59号)</h2>」のHTMLは「<h2 class=”search”>1974-01 (通巻59号)</h2>」となります。

関数 check_key

関数 check_keyは、テキスト内を検索キーワードで検索して、該当の有無を返します。

引数 説明
$text 検索するテキスト
$key 検索キーワード
戻り値 1 : 有り / 0 : 無し
キーワード検索

キーワード検索

  • 検索するテキスト$textの前後にスペースを追加します。
  • 検索キーワード$keyが数字のとき、”[^\d]$key[^\d]”にマッチするか調べます。
    これは、検索キーワードの前後が数字以外ということを示しています。
    その結果、キーワード「6800」は、「・・・MC68000・・・」というテキストにはマッチしません。
  • 検索キーワード$keyが数字以外のとき、”[^A-Z]$key[^A-Z]”にマッチするか調べます。
    これは、検索キーワードの前後が英字以外ということを示しています。
    その結果、キーワード「MAC」は、「・・・EMACS・・・」というテキストにはマッチしません。

なお、-match演算子は、英字の小文字と大文字を区別せずに比較しますので、小文字でも大文字でもマッチします。

メイン

  • 検索対象の年を定義します。 $yyyy_s=1974 、$yyyy_e=1992
  • 検索キーワード$keyを入力します。
  • 年$yyyyを$yyyy_s~$yyyy_eの間繰り返します。
    • アクセスするbit総合目次のURLを作ります。
    • 関数 search_keyで指定したURLにアクセスして、検索キーワードに該当したらbit誌情報を出力します。

bit目次検索の実行

PowerShellスクリプトを実行してみます。

キーワードとして「vax」を入力します。少し時間がかかりますが、以下のように指定したキーワードに該当するbit誌の情報を表示します。


PS E:\ps> .\bit_search.ps1
bit 総合目次 キーワード検索 Start!
Please input KEY word : vax

 vax
1982-07 32ビット・スーパーミニコンのOS [02]  VAX/VMS 今野肇 
1985-01 DEC,メインフレーム市場に進出  ――VAXファミリーの最上位機種発表 栗田昭平 
1986-12 Common Lispアラカルト [04]  VAX LISP 川合進 
1988-07 DECがVAXコンピュータ9機種を一挙に発表    栗田昭平 
1990-01 タンデムのCyclone,DECのVAX900発表で熾烈化するメインフレーム市場    栗田昭平 

PS E:\ps> 

便利なbit目次検索

bit総合目次は、bit誌の目次を閲覧できるので便利です。
それに加えて、このPowerShellスクリプトは、任意のキーワードで検索できるので、さらに便利になります。
みなさん、活用してください。