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`を使う。