技术笔记PowerShell 下载 Microsoft Store 的 UWP 安装包
狂犬主子PowerShell 下载 Microsoft Store 的 UWP 安装包
由于本人有自动下载 Microsoft Store 上的 UWP 安装包的需求,故写了一个 PowerShell 脚本,方便以后使用。
这边我们使用 https://store.rg-adguard.net/ 这个网站提供的 API,注意在实际使用中可能需要遵守网站的 robots.txt
协议。
获取 APP 信息
首先,我们需要知道这个 App 的相关信息,并且能够在 https://store.rg-adguard.net/ 中找到。
此网站提供了四种请求类型来指定到一个 APP:
- URL (Link)
Sample Data: https://www.microsoft.com/store/productId/9NSWSBXN8K03
- ProductId
Sample Data: 9NKSQGP7F2NH
- PackageFamilyName
Sample Data: Microsoft.WindowsStore_8wekyb3d8bbwe
- CategoryID
Sample Data: d58c3a5f-ca63-4435-842c-7814b5ff91b7
我个人是比较推荐用第3种PackageFamilyName的,因为搜索起来比较方便,以下脚本均是用此方式。
使用 Invoke-WebRequest 请求软件下载链接信息
使用 PowerShell 的 Invoke-WebRequest
,发送一个 POST 请求到 https://store.rg-adguard.net/api/GetFiles
接口,请求参数包括:
type
:请求类型,这里为’PackageFamilyName’
url
:应用ID,这里为’Microsoft.WindowsStore_8wekyb3d8bbwe’
ring
:渠道,这里为’RP’
lang
:语言,这里为’zh-CN’
这些参数与网页前端看到的相对应。
1 2 3 4 5 6 7 8 9
| $obj = Invoke-WebRequest -Uri "https://store.rg-adguard.net/api/GetFiles" ` -Method "POST" ` -ContentType "application/x-www-form-urlencoded" ` -Body @{ type = 'PackageFamilyName' url = 'Microsoft.WindowsStore_8wekyb3d8bbwe' ring = 'RP' lang = 'zh-CN' }
|
我们测试一下返回的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| PS > $obj
StatusCode : 200 StatusDescription : OK Content : <b>CategoryID:</b> <i>64293252-5926-453c-9494-2d4021f1c78d</i><center><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygo ogle.js"></script> <!-- Microsoft Store <ins class="ads… RawContent : HTTP/1.1 200 OK Date: Thu, 11 Apr 2024 16:48:02 GMT Transfer-Encoding: chunked Connection: keep-alive X-Powered-By: PHP/7.4.33 Strict-Transport-Security: max-age=31536000; CF-Cache-Status: DYNAM… Headers : {[Date, System.String[]], [Transfer-Encoding, System.String[]], [Connection, System.String[]], [X-Powered-By, System.String[]]…} Images : 省略 InputFields : {} Links : 省略 RawContentLength : 29436 RelationLink : {}
|
返回的内容不是 JSON 格式,而是一个 HTML 页面。
幸运的是,PowerShell 可以直接通过 $obj.Links
自动爬取 HTML 页面中的链接,然后通过 $obj.Links.href
获取下载链接。
1 2 3 4 5 6 7 8 9 10 11 12
| PS > $obj.Links
outerHTML --------- <a href="http://dl.delivery.mp.microsoft.com/filestreamingservice/files/xxxxxx" rel="noreferrer">Microsoft.NET.Native.Framework… ... <a href="http://dl.delivery.mp.microsoft.com/filestreamingservice/files/xxxxxx" rel="noreferrer">Microsoft.WindowsStore_22403.1…
PS > $obj.Links.href http://dl.delivery.mp.microsoft.com/filestreamingservice/files/xxxxxx http://tlu.dl.delivery.mp.microsoft.com/filestreamingservice/files/xxxxxx ...
|
不过 .href
输出的链接并包含文件名,我们还需对文件名和下载文件进行匹配。
匹配文件名和下载文件
此站的 API 会返回依赖的文件,这个对于用户是好事,可以避免缺少依赖导致无法安装,我们安装前面肯定是要先安装依赖再装软件的。
不过这里的问题在于如何把这些文件全部都下载下来,且保证文件和文件名对应,且部分文件是我们不需要的,这要求我们对脚本进行自定义,通过各种匹配机制,如 -like
、-match
、-contains
等,对内容进行一个筛选。且 PowerShell 解析的 HTML 不支持读取 innerText
,即 <a>
标签的文本内容,这给我们准确匹配并输出信息带来不便。
下面的例子我们以下载运行库 Microsoft.VCLibs.140.00
最新版本为例。
首先还是请求接口。
1 2 3 4 5 6 7 8 9
| $obj = Invoke-WebRequest -Uri "https://store.rg-adguard.net/api/GetFiles" ` -Method "POST" ` -ContentType "application/x-www-form-urlencoded" ` -Body @{ type = 'PackageFamilyName' url = 'Microsoft.VCLibs.140.00_8wekyb3d8bbwe' ring = 'RP' lang = 'zh-CN' }
|
然后执行 $obj.Links.outerHTML
,我们可以看到文件名了。
这个 API 返回的安装包列表存在大量信息需要我们过滤,比如说系统架构,假设我们是 x64
的系统,那边需要匹配到的不只是 x64
的,还有 neutral
的通用架构,所以我们再匹配前必须了解尽可能详细的信息。
另外,我们要下载的主程序安装包也会返回多个,根据经验,有些时候返回的是 .eappxbundle
格式的,这种是给 Xbox 的,安装包是加密了的,Windows 上用不了;有时还会返回多个格式的版本,比如说 .msixbundle
新版本 和 .appxbundle
老版本。
有些时候同样是 .appxbundle
,但是会返回多个版本号,最新版本的不兼容老系统:有些是 Windows 11 专用的,不能给 Windows 10 装。
不过这些都是能够根据实际情况定制脚本解决的。另外我相信 Github、PSGallary 上肯定有现成的模块,那种绝对比我们自己写的好很多。
实际情况我要同时下载 x64
、x86
两个平台,我们这边发现有很多包是用不上的,如 arm64
、arm
,另外包的格式为 .appx
,我们需要过滤掉 .BlockMap
。
这边使用 -match
正则表达式匹配:
1 2 3 4 5 6 7 8 9 10
| foreach ($link in $obj.Links) { if ($link.outerHTML -match '(?<=<a\b[^>]*>).*?(?=</a>)') { $linkText = $Matches[0] if ($linkText -match '(x86|x64).*\.appx\b') { Write-Output " $linkText : $($link.href)" } } }
|
到这里我们就可以看到安装包已经被正确解析并过滤出来了,这样我们就可以将 Write-Output
改为 Invoke-WebRequest
下载。
完整脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| function Download-Appx($Name) { $obj = Invoke-WebRequest -Uri "https://store.rg-adguard.net/api/GetFiles" ` -Method "POST" ` -ContentType "application/x-www-form-urlencoded" ` -Body @{ type = 'PackageFamilyName' url = $Name ring = 'RP' lang = 'zh-CN' }
foreach ($link in $obj.Links) { if ($link.outerHTML -match '(?<=<a\b[^>]*>).*?(?=</a>)') { $linkText = $Matches[0] if ($linkText -match '(x86|x64|neutral).*\.(appx|appxbundle|msixbundle)\b') { Write-Debug "$linkText : $($link.href)" if (Test-Path -Path $linkText) { Write-Warning "Already exists, skiping $linkText" } else { Invoke-WebRequest -Uri $link.href -OutFile $linkText } } } } }
Download-Appx 'Microsoft.VCLibs.140.00_8wekyb3d8bbwe'
Download-Appx 'Microsoft.AV1VideoExtension_8wekyb3d8bbwe' Download-Appx 'Microsoft.HEIFImageExtension_8wekyb3d8bbwe' Download-Appx 'Microsoft.MPEG2VideoExtension_8wekyb3d8bbwe' Download-Appx 'Microsoft.RawImageExtension_8wekyb3d8bbwe' Download-Appx 'Microsoft.VP9VideoExtensions_8wekyb3d8bbwe' Download-Appx 'Microsoft.WebMediaExtensions_8wekyb3d8bbwe' Download-Appx 'Microsoft.WebpImageExtension_8wekyb3d8bbwe'
|