PowerShell 技能连载 - Browser Use 浏览器自动化

适用于 PowerShell 7.0 及以上版本

背景

2026 年,浏览器自动化技术已经从简单的网页测试工具发展为 AI Agent 执行任务的核心能力。以 Browser Use 为代表的框架让 AI 模型能够直接操控浏览器完成复杂任务,例如自动填表、数据采集、跨站点流程编排等。Playwright 和 Selenium 作为两大主流浏览器自动化引擎,都提供了成熟的 API,而 PowerShell 凭借其强大的对象管道和 .NET 生态集成能力,成为串联这些工具的绝佳胶水语言。

在实际运维和数据处理场景中,我们经常需要从没有 API 的内部系统中提取数据、定时提交报表、或批量执行重复的网页操作。传统做法依赖手动操作或录制宏脚本,维护成本高且容易出错。PowerShell 结合 Playwright 可以编写声明式的自动化脚本,配合 AI 模型甚至能实现”说一句话,浏览器自动完成操作”的智能体验。

本文将分三个层次介绍 PowerShell 浏览器自动化:从 Playwright 基础操作,到数据采集与表单自动化,再到 AI 驱动的智能浏览器操作,帮助读者逐步掌握这项实用技能。

Playwright 自动化基础

Playwright 是微软开发的跨浏览器自动化框架,原生支持 Chromium、Firefox 和 WebKit。PowerShell 可以通过 .NET 互操作直接调用 Playwright 的 NuGet 包,实现从浏览器启动到元素操作的全流程控制。

以下代码演示了 Playwright 的安装、浏览器启动、页面导航、等待元素加载、点击按钮、读取文本等基本操作:

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
# 安装 Playwright NuGet 包和浏览器二进制文件
Install-Module -Name Microsoft.Playwright -Scope CurrentUser -Force
dotnet tool install --global Microsoft.Playwright.CLI
playwright install chromium

# 导入模块并启动浏览器
using module Microsoft.Playwright

$playwright = [Microsoft.Playwright.Program]::CreatePlaywright()
$browser = $playwright.Chromium.LaunchAsync(@{ headless = $true }).GetAwaiter().GetResult()

# 创建新页面并导航到目标网站
$page = $browser.NewPageAsync().GetAwaiter().GetResult()
$page.GotoAsync("https://example.com").GetAwaiter().GetResult()

# 等待页面标题加载并获取标题文本
$page.WaitForSelectorAsync("h1").GetAwaiter().GetResult() | Out-Null
$title = $page.TextContentAsync("h1").GetAwaiter().GetResult()
Write-Host "页面标题: $title"

# 查找链接并获取所有链接地址
$links = $page.QuerySelectorAllAsync("a").GetAwaiter().GetResult()
foreach ($link in $links) {
$href = $link.GetAttributeAsync("href").GetAwaiter().GetResult()
$text = $link.TextContentAsync().GetAwaiter().GetResult()
Write-Host " 链接: $text -> $href"
}

# 关闭浏览器释放资源
$browser.CloseAsync().GetAwaiter().GetResult()
$playwright.Dispose()

执行结果示例:

1
2
页面标题: Example Domain
链接: More information... -> https://www.iana.org/domains/example

在实际项目中,推荐使用 try-finally 块确保浏览器资源被正确释放,避免残留进程占用系统内存。

数据采集与表单自动化

网页数据提取和表单自动填写是浏览器自动化最常见的应用场景。PowerShell 可以将采集到的结构化数据直接转化为对象,利用管道进行筛选和导出,这是其他脚本语言难以比拟的优势。

下面的脚本展示了从网页表格提取数据、自动填写搜索表单、以及将页面保存为截图和 PDF 的完整流程:

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
37
38
39
40
41
42
43
44
using module Microsoft.Playwright

$playwright = [Microsoft.Playwright.Program]::CreatePlaywright()
$browser = $playwright.Chromium.LaunchAsync(@{ headless = $true }).GetAwaiter().GetResult()
$page = $browser.NewPageAsync().GetAwaiter().GetResult()

# 导航到包含表格的页面
$page.GotoAsync("https://example.com/tables").GetAwaiter().GetResult()
$page.WaitForSelectorAsync("table").GetAwaiter().GetResult() | Out-Null

# 提取表格数据并转化为 PowerShell 对象
$rows = $page.QuerySelectorAllAsync("table tbody tr").GetAwaiter().GetResult()
$tableData = foreach ($row in $rows) {
$cells = $row.QuerySelectorAllAsync("td").GetAwaiter().GetResult()
[PSCustomObject]@{
Name = $cells[0].TextContentAsync().GetAwaiter().GetResult().Trim()
Value = $cells[1].TextContentAsync().GetAwaiter().GetResult().Trim()
Status = $cells[2].TextContentAsync().GetAwaiter().GetResult().Trim()
}
}

# 筛选状态为 Active 的记录并导出为 CSV
$tableData | Where-Object { $_.Status -eq "Active" } | Export-Csv -Path "active_items.csv" -NoTypeInformation -Encoding utf8
Write-Host "已导出 $($tableData.Count) 条记录"

# 自动填写搜索表单
$page.GotoAsync("https://example.com/search").GetAwaiter().GetResult()
$page.FillAsync("#search-input", "PowerShell automation").GetAwaiter().GetResult()
$page.SelectOptionAsync("#category", "technology").GetAwaiter().GetResult()
$page.ClickAsync("#search-button").GetAwaiter().GetResult()

# 等待搜索结果加载
$page.WaitForSelectorAsync(".result-item").GetAwaiter().GetResult() | Out-Null

# 截取搜索结果页面的屏幕截图
$page.ScreenshotAsync(@{ path = "search-results.png"; fullPage = $true }).GetAwaiter().GetResult() | Out-Null
Write-Host "截图已保存到 search-results.png"

# 将当前页面导出为 PDF
$page.PdfAsync(@{ path = "search-results.pdf"; format = "A4" }).GetAwaiter().GetResult() | Out-Null
Write-Host "PDF 已保存到 search-results.pdf"

$browser.CloseAsync().GetAwaiter().GetResult()
$playwright.Dispose()

执行结果示例:

1
2
3
已导出 12 条记录
截图已保存到 search-results.png
PDF 已保存到 search-results.pdf

通过 PSCustomObject 将网页数据结构化后,可以无缝使用 Where-ObjectSort-ObjectExport-Csv 等 PowerShell 原生命令进行后续处理,实现从采集到入库的一体化流程。

AI 驱动的浏览器操作

2026 年最具变革性的趋势是将大语言模型(LLM)与浏览器自动化结合,让 AI 理解页面语义后自动决定操作步骤。PowerShell 在这个场景中扮演调度器的角色:调用 LLM API 分析页面结构,解析返回的指令,再驱动浏览器执行具体动作。

以下脚本展示了如何用 PowerShell 调用本地 Ollama 模型分析网页元素,实现 AI 驱动的智能浏览器操作:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using module Microsoft.Playwright

# 配置 Ollama API 端点
$ollamaEndpoint = "http://localhost:11434/api/generate"
$modelName = "qwen2.5:7b"

function Invoke-OllamaChat {
param([string]$Prompt)
$body = @{
model = $modelName
prompt = $Prompt
stream = $false
} | ConvertTo-Json -Depth 5

$response = Invoke-RestMethod -Uri $ollamaEndpoint -Method Post -Body $body -ContentType "application/json"
return $response.response
}

# 启动浏览器并导航到目标页面
$playwright = [Microsoft.Playwright.Program]::CreatePlaywright()
$browser = $playwright.Chromium.LaunchAsync(@{ headless = $false }).GetAwaiter().GetResult()
$page = $browser.NewPageAsync().GetAwaiter().GetResult()
$page.GotoAsync("https://news.example.com").GetAwaiter().GetResult()

# 提取页面可交互元素的摘要信息
$elements = $page.QuerySelectorAllAsync("a, button, input, select").GetAwaiter().GetResult()
$elementSummary = foreach ($el in $elements[0..19]) {
$tag = $el.EvaluateHandleAsync("e => e.tagName").GetAwaiter().GetResult().JsonValue
$text = $el.TextContentAsync().GetAwaiter().GetResult()
$role = $el.GetAttributeAsync("role").GetAwaiter().GetResult()
" <$tag> text='$($text.Trim().Substring(0, [Math]::Min(50, $text.Trim().Length)))' role='$role'"
}
$summaryText = $elementSummary -join "`n"

# 让 AI 分析页面并推荐操作
$aiPrompt = @"
你是一个浏览器自动化助手。以下是页面上的可交互元素:

$summaryText

用户任务:找到今天浏览量最高的科技新闻标题。
请告诉我应该点击哪个元素(用序号表示),以及后续操作建议。
只返回简洁的操作指令,不要解释。
"@

$aiResponse = Invoke-OllamaChat -Prompt $aiPrompt
Write-Host "AI 建议: $aiResponse"

# 根据 AI 建议提取操作索引并执行点击
if ($aiResponse -match "点击.*?(\d+)") {
$index = [int]$Matches[1]
if ($index -lt $elements.Count) {
$elements[$index].ClickAsync().GetAwaiter().GetResult() | Out-Null
Write-Host "已执行 AI 建议的点击操作(元素 #$index)"
}
}

# 等待新页面加载后提取标题
Start-Sleep -Seconds 2
$articleTitle = $page.TitleAsync().GetAwaiter().GetResult()
Write-Host "当前页面标题: $articleTitle"

$browser.CloseAsync().GetAwaiter().GetResult()
$playwright.Dispose()

执行结果示例:

1
2
3
AI 建议: 点击元素 #3,这是"科技"分类链接。进入后点击第一个新闻标题即可。
已执行 AI 建议的点击操作(元素 #3)
当前页面标题: 最新科技新闻 - News Example

这种方式将 LLM 的语义理解能力与 Playwright 的精确操作能力结合,实现了”自然语言到浏览器操作”的闭环。在生产环境中,建议加入操作确认机制和异常回退逻辑,确保 AI 的操作可预测且可追溯。

注意事项

  1. 资源管理:Playwright 的浏览器实例是重量级资源,务必使用 try-finally 块确保 CloseAsyncDispose 被调用,避免残留进程消耗系统资源。

  2. 异步处理:Playwright 的 .NET API 大量使用 async/await 模式,在 PowerShell 中需要通过 .GetAwaiter().GetResult() 同步等待。避免在循环中频繁调用,可改用批量操作减少开销。

  3. 等待策略:页面加载时间受网络状况影响较大,推荐使用 WaitForSelectorAsync 替代固定的 Start-Sleep,既保证元素可用,又避免不必要的等待。

  4. 无头模式选择:开发调试时使用 headless = $false 以便观察浏览器行为,生产环境使用 headless = $true 提升性能。在 Linux 服务器上运行时需要安装额外的系统依赖库。

  5. AI 操作安全性:让 LLM 直接控制浏览器操作具有不确定性,务必对 AI 返回的指令进行校验(如索引范围检查、URL 白名单过滤),防止误操作导致数据丢失或安全风险。

  6. 反爬虫应对:部分网站会检测自动化工具并限制访问。可通过设置 User-Agent、使用浏览器上下文隔离、控制请求频率等方式降低被识别的概率,同时确保遵守目标网站的服务条款。

PowerShell 技能连载 - Web 数据采集

适用于 PowerShell 7.0 及以上版本

在运维和数据分析工作中,经常需要从网页上采集数据——监控页面上的状态信息、采集竞争对手的价格数据、抓取内部系统的报表。这些场景看似简单,但手动操作既耗时又容易出错,尤其是当数据源多、更新频繁时,人工采集几乎无法持续。

PowerShell 通过 Invoke-WebRequest 和内置的 HTML 解析能力,可以快速构建轻量级的数据采集脚本。与 Python 的 Scrapy 或 BeautifulSoup 相比,PowerShell 方案无需额外安装解释器,直接在 Windows 或跨平台环境中即可运行,特别适合已经在使用 PowerShell 进行运维自动化的团队。

本文将从基础的 HTML 解析入手,逐步介绍表单提交与认证采集,最后实现批量并发采集与数据清洗的完整方案,帮助你构建可靠的数据采集管道。

HTML 解析与数据提取

Invoke-WebRequest 返回的对象中包含一个 ParsedHtml 属性,但跨平台场景下更推荐使用正则表达式或 HTML Agility Pack 来解析。下面演示如何从一个示例页面中提取表格数据。

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
37
# 从示例页面采集表格数据
$url = "https://example.com/status"
$response = Invoke-WebRequest -Uri $url -UseBasicParsing

# 方法一:使用正则表达式提取 HTML 表格中的数据
$pattern = '<tr>\s*<td>(.*?)</td>\s*<td>(.*?)</td>\s*<td>(.*?)</td>\s*</tr>'
$matches = [regex]::Matches($response.Content, $pattern)

$results = foreach ($m in $matches) {
[PSCustomObject]@{
ServerName = $m.Groups[1].Value.Trim()
Status = $m.Groups[2].Value.Trim()
ResponseMs = $m.Groups[3].Value.Trim()
}
}

$results | Format-Table -AutoSize

# 方法二:使用 HTML Agility Pack 进行 CSS 选择器查询
# 先安装 HTML Agility Pack
Install-Module -Name HtmlAgilityPack -Force -Scope CurrentUser -ErrorAction SilentlyContinue
Add-Type -Path (Join-Path $env:USERPROFILE ".nuget\packages\htmlagilitypack\1.11.72\lib\netstandard2.0\HtmlAgilityPack.dll") -ErrorAction SilentlyContinue

# 使用 HtmlDocument 解析
$doc = [HtmlAgilityPack.HtmlDocument]::new()
$doc.LoadHtml($response.Content)

# 通过 XPath 精确定位
$nodes = $doc.DocumentNode.SelectNodes('//table[@class="status-table"]//tr[position()>1]')
foreach ($row in $nodes) {
$cells = $row.SelectNodes('.//td')
[PSCustomObject]@{
ServerName = $cells[0].InnerText.Trim()
Status = $cells[1].InnerText.Trim()
ResponseMs = $cells[2].InnerText.Trim()
}
}

执行结果示例:

1
2
3
4
5
6
7
ServerName Status   ResponseMs
---------- ------ ----------
WEB-01 Online 45
WEB-02 Online 38
DB-01 Online 120
DB-02 Offline N/A
CACHE-01 Online 12

表单提交与认证采集

很多数据藏在需要登录的系统后面。PowerShell 的 WebRequestSession 对象可以自动管理 Cookie,模拟完整的登录流程后访问受保护的页面。

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
37
38
39
# 创建会话变量,用于保持登录状态
$loginUrl = "https://internal.example.com/login"
$reportUrl = "https://internal.example.com/report/daily"

# 第一步:获取登录页面(同时初始化 Session)
$loginPage = Invoke-WebRequest -Uri $loginUrl -SessionVariable session -UseBasicParsing

# 第二步:提取隐藏的表单字段(如 CSRF Token)
$csrfPattern = 'name="__RequestVerificationToken".*?value="([^"]+)"'
$csrfMatch = [regex]::Match($loginPage.Content, $csrfPattern)
$csrfToken = if ($csrfMatch.Success) { $csrfMatch.Groups[1].Value } else { "" }

# 第三步:提交登录表单
$loginBody = @{
username = "admin"
password = "P@ssw0rd123!"
__RequestVerificationToken = $csrfToken
RememberMe = "true"
}

$loginResponse = Invoke-WebRequest -Uri $loginUrl -Method POST -Body $loginBody -WebSession $session -UseBasicParsing

# 第四步:使用已认证的会话访问报表页面
$reportResponse = Invoke-WebRequest -Uri $reportUrl -WebSession $session -UseBasicParsing

# 第五步:从报表页面提取数据
$dataPattern = '<tr[^>]*>\s*<td>(\d{4}-\d{2}-\d{2})</td>\s*<td>([\d,.]+)</td>\s*<td>([\d,.]+)</td>\s*</tr>'
$dataMatches = [regex]::Matches($reportResponse.Content, $dataPattern)

$reportData = foreach ($m in $dataMatches) {
[PSCustomObject]@{
Date = $m.Groups[1].Value
Revenue = $m.Groups[2].Value
Cost = $m.Groups[3].Value
}
}

$reportData | Format-Table -AutoSize
Write-Host "`n采集完成,共获取 $($reportData.Count) 条记录"

执行结果示例:

1
2
3
4
5
6
7
8
9
Date       Revenue       Cost
---- ------- ----
2026-01-30 125,680.00 89,340.00
2026-01-31 132,450.00 91,200.00
2026-02-01 118,930.00 87,650.00
2026-02-02 141,200.00 93,100.00
2026-02-03 156,780.00 95,420.00

采集完成,共获取 5 条记录

批量采集与数据清洗

当需要从多个页面或多个数据源采集数据时,可以利用 PowerShell 的 ForEach-Object -Parallel 实现并发采集,然后对原始数据进行清洗和标准化处理,最后导出为结构化格式。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 定义采集目标列表
$targets = @(
@{ Name = "北京"; Url = "https://api.example.com/weather/beijing" }
@{ Name = "上海"; Url = "https://api.example.com/weather/shanghai" }
@{ Name = "广州"; Url = "https://api.example.com/weather/guangzhou" }
@{ Name = "深圳"; Url = "https://api.example.com/weather/shenzhen" }
@{ Name = "成都"; Url = "https://api.example.com/weather/chengdu" }
)

# 并发采集数据(限制并发数为 3)
$rawData = $targets | ForEach-Object -ThrottleLimit 3 -Parallel {
$target = $_
try {
$response = Invoke-WebRequest -Uri $target.Url -UseBasicParsing -TimeoutSec 10
# 解析 JSON 响应
$json = $response.Content | ConvertFrom-Json
[PSCustomObject]@{
City = $target.Name
Temperature = $json.temperature
Humidity = $json.humidity
Wind = $json.wind
AQI = $json.aqi
Status = "Success"
ErrorMsg = ""
}
}
catch {
[PSCustomObject]@{
City = $target.Name
Temperature = $null
Humidity = $null
Wind = $null
AQI = $null
Status = "Failed"
ErrorMsg = $_.Exception.Message
}
}
}

# 数据清洗:过滤失败记录,标准化数值
$cleanData = $rawData | Where-Object { $_.Status -eq "Success" } | ForEach-Object {
# 温度转摄氏度(假设原始数据为华氏度)
$tempC = [math]::Round(($_.Temperature - 32) * 5 / 9, 1)
# 湿度取整
$humidity = [int]$_.Humidity

[PSCustomObject]@{
City = $_.City
TemperatureC = $tempC
HumidityPct = $humidity
WindSpeed = $_.Wind
AQI = [int]$_.AQI
AQILevel = switch ([int]$_.AQI) {
{ $_ -le 50 } { "优" }
{ $_ -le 100 } { "良" }
{ $_ -le 150 } { "轻度污染" }
{ $_ -le 200 } { "中度污染" }
default { "重度污染" }
}
CollectTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}

# 显示结果
$cleanData | Format-Table -AutoSize

# 导出到 CSV
$csvPath = Join-Path $env:USERPROFILE "Desktop\weather_data.csv"
$cleanData | Export-Csv -Path $csvPath -NoTypeInformation -Encoding Utf8
Write-Host "CSV 已导出至: $csvPath"

# 导出到 JSON
$jsonPath = Join-Path $env:USERPROFILE "Desktop\weather_data.json"
$cleanData | ConvertTo-Json -Depth 3 | Set-Content -Path $jsonPath -Encoding Utf8
Write-Host "JSON 已导出至: $jsonPath"

执行结果示例:

1
2
3
4
5
6
7
8
9
10
City  TemperatureC HumidityPct WindSpeed AQI AQILevel CollectTime
---- ------------ ----------- --------- --- -------- -----------
北京 2.3 45 3.2m/s 82 良 2026-02-05 08:15:32
上海 6.8 62 4.1m/s 68 良 2026-02-05 08:15:33
广州 15.2 78 2.8m/s 55 良 2026-02-05 08:15:33
深圳 17.6 75 3.0m/s 48 优 2026-02-05 08:15:33
成都 8.1 70 1.5m/s 92 良 2026-02-05 08:15:34

CSV 已导出至: C:\Users\admin\Desktop\weather_data.csv
JSON 已导出至: C:\Users\admin\Desktop\weather_data.json

注意事项

  1. 遵守 robots.txt 和法律合规:采集前检查目标网站的 robots.txt 文件,尊重网站的爬取规则。对敏感数据或受版权保护的内容,务必获得授权后再采集,避免触犯法律。

  2. 请求频率控制:高频率请求可能对目标服务器造成压力,甚至触发 IP 封禁。建议在循环中加入 Start-Sleep -Milliseconds 500 等延迟,并发采集时将 ThrottleLimit 控制在 3-5 之间。

  3. 异常处理与重试机制:网络请求天然不稳定,务必使用 try/catch 包裹所有请求代码,并实现指数退避重试策略,确保脚本在偶发网络故障时不会中断。

  4. User-Agent 伪装:部分网站会拒绝默认的 PowerShell User-Agent。可以通过 -Headers @{ 'User-Agent' = 'Mozilla/5.0 ...' } 设置合理的浏览器标识,但这并不意味着可以绕过反爬机制去做不当采集。

  5. 编码与字符集处理:中文网页常见 GBK、GB2312 等编码,PowerShell 默认使用 UTF-8。采集后需要用 [System.Text.Encoding]::GetEncoding('GBK').GetString(...) 进行转码,否则会出现乱码。

  6. 数据验证与清洗:从网页提取的原始数据通常包含空白字符、HTML 实体(如 &amp;&nbsp;)和格式不一致的数值。建议封装一个 Invoke-DataClean 函数统一处理这些常见问题,确保导出数据的规范性。

PowerShell 技能连载 - Web 数据采集

适用于 PowerShell 5.1 及以上版本

运维和开发中经常需要从网页获取数据——监控服务状态页、采集系统指标、下载最新版本的工具、从内部管理平台提取报表。PowerShell 内置的 Invoke-WebRequest 可以发送 HTTP 请求并解析 HTML,结合正则表达式和 HTML 解析能力,可以高效完成大部分数据采集任务。

本文将讲解 HTTP 请求、HTML 解析、表单提交、会话管理,以及浏览器自动化技术。

HTTP 请求基础

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
# 基本 GET 请求
$response = Invoke-WebRequest -Uri "https://httpbin.org/get" -UseBasicParsing
Write-Host "状态码:$($response.StatusCode)"
Write-Host "内容长度:$($response.Content.Length) bytes"

# 解析 JSON 响应
$json = $response.Content | ConvertFrom-Json
Write-Host "来源 IP:$($json.origin)"
Write-Host "User-Agent:$($json.headers.'User-Agent')"

# 自定义请求头
$headers = @{
"User-Agent" = "PowerShell/7.4 (OpsBot)"
"Accept" = "application/json"
"X-Custom-Header" = "monitoring"
}

$response = Invoke-WebRequest -Uri "https://httpbin.org/headers" `
-Headers $headers -UseBasicParsing
$json = $response.Content | ConvertFrom-Json
$json.headers | Format-Table -AutoSize

# POST 请求(JSON 数据)
$body = @{
hostname = $env:COMPUTERNAME
status = "healthy"
uptime = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime.ToString("o")
} | ConvertTo-Json

$response = Invoke-WebRequest -Uri "https://httpbin.org/post" `
-Method Post `
-ContentType "application/json; charset=utf-8" `
-Body ([System.Text.Encoding]::UTF8.GetBytes($body)) `
-UseBasicParsing

Write-Host "POST 响应:$($response.StatusCode)"

执行结果示例:

1
2
3
4
5
6
7
8
9
10
状态码:200
内容长度:356 bytes
来源 IP:203.0.113.42
User-Agent:PowerShell/7.4 (OpsBot)

Host User-Agent X-Custom-Header
---- ---------- ---------------
httpbin.org PowerShell/7.4 (OpsBot) monitoring

POST 响应:200

HTML 解析

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 解析 HTML 页面提取链接
function Get-WebLinks {
param(
[Parameter(Mandatory)]
[string]$Url,
[string]$Pattern = ".*"
)

$response = Invoke-WebRequest -Uri $Url -UseBasicParsing

# 从 HTML 中提取所有链接
$links = [regex]::Matches($response.Content, 'href=["''](https?://[^"'']+)["'']') |
ForEach-Object { $_.Groups[1].Value } |
Where-Object { $_ -match $Pattern } |
Sort-Object -Unique

return $links
}

# 提取页面中的所有下载链接
$downloadLinks = Get-WebLinks -Url "https://example.com/downloads" -Pattern "\.msi$|\.zip$|\.exe$"
$downloadLinks | ForEach-Object { Write-Host " $_" -ForegroundColor Cyan }

# 使用 HTML 解析提取表格数据
function Get-HtmlTable {
param(
[Parameter(Mandatory)]
[string]$Url,
[int]$TableIndex = 0
)

$response = Invoke-WebRequest -Uri $Url -UseBasicParsing
$html = $response.Content

# 提取表格行
$rows = [regex]::Matches($html, '<tr[^>]*>(.*?)</tr>', [System.Text.RegularExpressions.RegexOptions]::Singleline)

$tableData = foreach ($row in $rows) {
$cells = [regex]::Matches($row.Groups[1].Value, '<t[dh][^>]*>(.*?)</t[dh]>', [System.Text.RegularExpressions.RegexOptions]::Singleline)
$cellValues = $cells | ForEach-Object {
# 去除 HTML 标签
$val = $_.Groups[1].Value -replace '<[^>]+>', ''
$val.Trim()
}
if ($cellValues) {
$cellValues -join '|'
}
}

return $tableData
}

$tableData = Get-HtmlTable -Url "https://example.com/status"
$tableData | ForEach-Object { Write-Host $_ }

执行结果示例:

1
2
3
4
5
6
7
  https://example.com/downloads/tool-v3.2.1.msi
https://example.com/downloads/tool-v3.2.1.zip

服务名|状态|响应时间
Auth-Service|Running|45ms
API-Gateway|Running|23ms
Worker-01|Stopped|N/A

会话管理与认证

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
37
38
39
40
41
42
# 使用 WebRequestSession 维持会话
$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
$session.UserAgent = "PowerShell-OpsBot/1.0"

# 模拟登录(第一步:获取登录页面)
$loginPage = Invoke-WebRequest -Uri "https://example.com/login" `
-WebSession $session -UseBasicParsing

# 提取 CSRF Token
$csrfToken = if ($loginPage.Content -match 'name="csrf_token"\s+value="([^"]+)"') {
$Matches[1]
} else {
""
}

# 第二步:提交登录表单
$loginBody = @{
username = "admin"
password = "P@ssw0rd123"
csrf_token = $csrfToken
}

$loginResponse = Invoke-WebRequest -Uri "https://example.com/login" `
-Method Post `
-Body $loginBody `
-WebSession $session `
-UseBasicParsing

Write-Host "登录状态:$($loginResponse.StatusCode)"

# 第三步:使用会话访问受保护页面
$dashboard = Invoke-WebRequest -Uri "https://example.com/dashboard" `
-WebSession $session -UseBasicParsing

Write-Host "仪表板内容长度:$($dashboard.Content.Length) bytes"

# Bearer Token 认证
$token = $env:API_TOKEN
$protectedData = Invoke-RestMethod -Uri "https://api.example.com/v1/status" `
-Headers @{ "Authorization" = "Bearer $token" }

$protectedData | Format-Table -AutoSize

执行结果示例:

1
2
3
4
5
6
7
8
登录状态:200
仪表板内容长度:45230 bytes

Service Status Uptime
------- ------ ------
auth-service Running 45d 12h
api-gateway Running 45d 12h
worker-01 Running 30d 8h

监控网页状态

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function Test-WebPageHealth {
<#
.SYNOPSIS
监控网页可用性和关键内容
#>
param(
[Parameter(Mandatory)]
[string[]]$Urls,

[string]$ExpectedContent = "",

[int]$TimeoutSeconds = 10
)

$results = foreach ($url in $Urls) {
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

try {
$response = Invoke-WebRequest -Uri $url `
-TimeoutSec $TimeoutSeconds `
-UseBasicParsing `
-ErrorAction Stop

$stopwatch.Stop()

$contentOk = if ($ExpectedContent) {
$response.Content -match [regex]::Escape($ExpectedContent)
} else {
$true
}

[PSCustomObject]@{
Url = $url
Status = $response.StatusCode
LatencyMs = $stopwatch.ElapsedMilliseconds
ContentMatch = $contentOk
ContentLen = $response.Content.Length
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
} catch {
$stopwatch.Stop()
[PSCustomObject]@{
Url = $url
Status = "Error"
LatencyMs = $stopwatch.ElapsedMilliseconds
ContentMatch = $false
ContentLen = 0
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
}
}

$results | Format-Table -AutoSize

$failed = $results | Where-Object { $_.Status -ne 200 -or -not $_.ContentMatch }
if ($failed) {
Write-Host "`n告警:$($failed.Count) 个页面异常" -ForegroundColor Red
$failed | ForEach-Object { Write-Host " $($_.Url) - Status: $($_.Status)" -ForegroundColor Red }
}
}

# 监控多个服务端点
Test-WebPageHealth -Urls @(
"https://blog.vichamp.com"
"https://api.example.com/health"
"https://portal.example.com"
) -ExpectedContent "OK"

执行结果示例:

1
2
3
4
5
6
7
8
Url                              Status LatencyMs ContentMatch ContentLen Timestamp
--- ------ --------- ------------ ---------- ---------
https://blog.vichamp.com 200 245 True 52301 2025-06-27 09:15:30
https://api.example.com/health 200 128 True 256 2025-06-27 09:15:31
https://portal.example.com Error 0 False 0 2025-06-27 09:15:41

告警:1 个页面异常
https://portal.example.com - Status: Error

文件下载管理

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
37
38
39
40
41
42
43
44
45
function Start-FileDownload {
<#
.SYNOPSIS
带进度显示的文件下载
#>
param(
[Parameter(Mandatory)]
[string]$Url,

[string]$OutputPath = (Join-Path $PWD (Split-Path $Url -Leaf)),

[int]$TimeoutSeconds = 300
)

Write-Host "下载:$Url" -ForegroundColor Cyan
Write-Host "保存到:$OutputPath"

try {
$response = Invoke-WebRequest -Uri $Url -OutFile $OutputPath `
-TimeoutSec $TimeoutSeconds -UseBasicParsing -PassThru

$file = Get-Item $OutputPath
$sizeMB = [math]::Round($file.Length / 1MB, 2)
Write-Host "下载完成:$sizeMB MB" -ForegroundColor Green

return $OutputPath
} catch {
Write-Host "下载失败:$($_.Exception.Message)" -ForegroundColor Red
return $null
}
}

# 批量下载
$files = @(
@{ Url = "https://example.com/data/report-june.csv"; Name = "report-june.csv" },
@{ Url = "https://example.com/data/report-may.csv"; Name = "report-may.csv" }
)

$downloadDir = "C:\Downloads\reports"
New-Item $downloadDir -ItemType Directory -Force | Out-Null

foreach ($file in $files) {
$output = Join-Path $downloadDir $file.Name
Start-FileDownload -Url $file.Url -OutputPath $output
}

执行结果示例:

1
2
3
4
5
6
下载:https://example.com/data/report-june.csv
保存到:C:\Downloads\reports\report-june.csv
下载完成:2.35 MB
下载:https://example.com/data/report-may.csv
保存到:C:\Downloads\reports\report-may.csv
下载完成:1.87 MB

注意事项

  1. 遵守 robots.txt:采集前检查目标网站的 robots.txt 和使用条款,尊重爬取规则
  2. 请求频率:添加合理的 Start-Sleep 间隔,避免对目标服务器造成压力
  3. 编码处理:网页编码可能不一致,使用 -UseBasicParsing 并手动处理编码
  4. 证书验证:内网自签名证书环境使用 -SkipCertificateCheck(PowerShell 7+)
  5. 数据敏感:采集到的数据可能包含敏感信息,注意脱敏和存储安全
  6. User-Agent:设置合理的 User-Agent,部分网站会屏蔽默认的 PowerShell UA