だるろぐ

明日できることは、今日しない。

PowerShell:ストアアプリのセール情報を取得する

# スクリプトと同じパスにある StoreApps.txt から URL を読み込む
$path = $PSScriptRoot | Join-Path -ChildPath "StoreApps.txt"
$urls = (Get-Content $path) -as [string[]]

# デバッグ用のサンプル
# $urls =@(
# "https://www.microsoft.com/ja-jp/store/p/nextgen-reader/9wzdncrfj262"
# )

foreach ($url in $urls)
{
    try
    {
        $request = Invoke-WebRequest $url
        
        # アプリ名を取得
        $title = $request.AllElements.FindById("page-title").innerText

        # ParsedHtml もめっちゃ便利やったぞ!
        # 例:打消し線の付いた定価タグを取得
        $body = $request.ParsedHtml
        $price_node = $body.getElementsByTagName("s") | where { 
            $_.getAttributeNode("class").Value  -eq "srv_saleprice"
        }

        # 定価が打ち消されていたらセール中ってこと
        if ($price_node)
        {
            $price = $price_node.innerText

            # 販売価格(セール価格)の取得
            $sales_node = $body.getElementsByTagName("meta") | where {
                $_.getAttributeNode("itemprop").Value  -eq "price"
            }
            $sales = "¥{0:#,0}"
                -f [int]$sales_node.getAttributeNode("content").Value

            # セール期間を取得
            $countdown_node = $body.getElementsByTagName("div") | where {
                $_.getAttributeNode("class").Value
                -eq "caption text-muted srv_countdown"
            }
            $countdown = $countdown_node.innerText
            $countdown = $countdown.Replace(" • ", "").Trim()

            # デバッグに使ってた
            # [PSCustomObject] @{
            #    Title = $title; Sales = $sales;
            #    Price = $price; Url = $url
            # }

# 今回は成形したはてな記法テキストを出力
@"
* $title

<s>$price</s> → <b>$sales</b>($countdown

$url`:embed

"@
        }
    
    }
    catch
    {
        Write-Host $Error[0] $url
    }
    finally
    {
        $sales_node = $null
        $sales = $null
        $price_node = $null
        $price = $null
    }
}

結果はこんな感じ。

* Nextgen Reader

<s>¥200</s> → <b>&yen;100</b>(¥100 値引き  あと 7 日です)

https://www.microsoft.com/ja-jp/store/p/nextgen-reader/9wzdncrfj262:embed

* Minecraft: Windows 10 Edition

<s>¥3,150</s> → <b>&yen;1,150</b>(¥2,000 値引き  あと 17 日です)

https://www.microsoft.com/ja-jp/store/p/minecraft-windows-10-edition/9nblggh2jhxj:embed

これをそのまま投稿するとこんな記事になりました。


今日学んだこと

スクリプトのあるフォルダーを取得する

PowerScript v3 以降では

$PSScriptRoot

が利用できる。それ以前だと、ちょっとめんどい(といってもひと手間増える程度だけど

キャストっぽいことをする

型演算子 -as が使える。割と柔軟に使えるみたいだけど、俺みたいな万年初心者には、どこまで柔軟にやってくれるのかよくわかんないのが不安。

$urls = (Get-Content $path) -as [string[]]

失敗すると $null が返る。親戚として -is、-isnot もチェック!

指定したクラスのタグを取得する

たとえば s.srv_saleprice は以下のコードでとれる。

$price_node = $request.ParsedHtml.getElementsByTagName("s") | where { 
    $_.getAttributeNode("class").Value  -eq "srv_saleprice"
}
$price_node.innerText

ちなみに、クラスが"caption text-muted srv_countdown"みたいに複数指定されてるときは"srv_countdown"だけで -eq 判定してもダメ。全体で -eq 評価するか、-match を使う。

まぁ、デフォルトでここまでできるのは便利だけど、それ以上はいろいろめんどいし、そろそろ HtmlAgilityPack でやるかなー。AllElements でとったタグの innerText を読むと改行が飛ぶといった挙動もあまり気に入らない(正規表現でお茶を濁した)。

ヒアドキュメント

@"
ヒアドキュメント $url 変数も評価される
$url`:embed(はてな記法
"@

変数に“:”が続くとそのまま評価されてしまう。困る場合は、バッククォートでエスケープすればいいみたい。

ちなみに、変数を評価してほしくない場合はシングルクォートでくくる。

はてな記法で PowerShell を構文色分け

コード記法で`ps1`を使う。