本文详细介绍 OpenClaw Canvas 的截图功能。从基本截图、全页面捕获、元素截图到图像处理,全面解析如何通过 Canvas 实现灵活的页面捕获。通过实际案例演示报告生成、内容存档、错误记录等场景,帮助开发者掌握 Canvas 截图的实际应用。
1. 引言 - 截图功能的价值
1.1 截图应用场景
| 场景 |
说明 |
示例 |
| 报告生成 |
生成可视化报告 |
数据报告截图 |
| 内容存档 |
保存页面内容 |
网页快照 |
| 错误记录 |
记录错误状态 |
Bug截图 |
| 分享展示 |
分享界面内容 |
成果展示 |
| 自动化测试 |
测试结果验证 |
UI测试截图 |
1.2 截图功能概览

1.3 支持的输出格式
| 格式 |
说明 |
适用场景 |
| PNG |
无损压缩 |
需要高质量 |
| JPG/JPEG |
有损压缩 |
文件较小 |
2. 基本截图操作
2.1 全页面截图
|
1
2
3
4
|
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
|
2.2 指定格式
|
1
2
3
4
5
6
7
8
9
10
11
|
# PNG格式
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
# JPG格式
screenshot = canvas(
action="snapshot",
outputFormat="jpg"
)
|
2.3 设置质量
|
1
2
3
4
5
|
screenshot = canvas(
action="snapshot",
outputFormat="jpg",
quality=80 # 1-100
)
|
2.4 设置尺寸
|
1
2
3
4
5
|
screenshot = canvas(
action="snapshot",
width=1920,
height=1080
)
|
3. 截图参数详解
3.1 参数列表
| 参数 |
类型 |
说明 |
| outputFormat |
string |
输出格式:png/jpg |
| quality |
number |
质量(1-100,仅JPG) |
| width |
number |
截图宽度 |
| height |
number |
截图高度 |
| x |
number |
起始X坐标 |
| y |
number |
起始Y坐标 |
3.2 区域截图
|
1
2
3
4
5
6
7
8
|
# 截取指定区域
screenshot = canvas(
action="snapshot",
x=100,
y=100,
width=800,
height=600
)
|
3.3 返回值
截图返回的是图像数据,可以直接保存或处理。
|
1
2
3
4
5
6
|
# 返回格式示例
{
"type": "image",
"format": "png",
"data": "base64编码的图像数据"
}
|
4. 实战案例一:报告截图
4.1 场景描述
生成数据报告并截图保存。
4.2 实现代码
|
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
76
77
78
79
80
81
82
83
|
def generate_report_screenshot(report_data):
"""生成报告截图"""
# 1. 创建报告界面
html = f"""
<!DOCTYPE html>
<html>
<head>
<style>
body {{
font-family: Arial, sans-serif;
padding: 40px;
background: white;
}}
.report {{
max-width: 800px;
margin: 0 auto;
}}
h1 {{
color: #333;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}}
.stats {{
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin: 30px 0;
}}
.stat-card {{
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}}
.stat-value {{
font-size: 2em;
font-weight: bold;
color: #667eea;
}}
.chart {{
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}}
</style>
</head>
<body>
<div class="report">
<h1>???? 数据报告</h1>
<p>生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<div class="stats">
<div class="stat-card">
<div class="stat-value">{report_data['total']}</div>
<div>总数</div>
</div>
<div class="stat-card">
<div class="stat-value">{report_data['success']}</div>
<div>成功</div>
</div>
<div class="stat-card">
<div class="stat-value">{report_data['rate']}%</div>
<div>成功率</div>
</div>
</div>
<div class="chart">
<h3>趋势图</h3>
<p>图表内容...</p>
</div>
</div>
</body>
</html>
"""
# 2. 展示报告
canvas(action="present", html=html)
# 3. 等待渲染
time.sleep(1)
# 4. 截图
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
return screenshot
|
5. 实战案例二:网页存档
5.1 场景描述
保存网页快照用于存档。
5.2 实现代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def archive_webpage(url):
"""存档网页"""
# 1. 加载网页
canvas(action="navigate", url=url)
# 2. 等待加载
time.sleep(3)
# 3. 截图
screenshot = canvas(
action="snapshot",
outputFormat="png",
width=1920,
height=1080
)
# 4. 保存
filename = f"archive_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
save_screenshot(screenshot, filename)
return filename
def save_screenshot(screenshot, filename):
"""保存截图"""
# 根据返回格式保存
# 可能是base64或直接是文件数据
with open(filename, 'wb') as f:
f.write(screenshot['data'])
|
6. 实战案例三:错误记录
6.1 场景描述
捕获错误状态用于调试。
6.2 实现代码
|
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
|
def capture_error_state(error_info):
"""捕获错误状态"""
# 1. 创建错误报告界面
html = f"""
<!DOCTYPE html>
<html>
<head>
<style>
body {{
font-family: monospace;
padding: 20px;
background: #1a1a2e;
color: #eee;
}}
.error-box {{
background: #16213e;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #e74c3c;
}}
.error-title {{
color: #e74c3c;
font-size: 1.5em;
margin-bottom: 10px;
}}
.error-time {{
color: #888;
margin-bottom: 20px;
}}
.error-message {{
background: #0f0f23;
padding: 15px;
border-radius: 4px;
white-space: pre-wrap;
}}
</style>
</head>
<body>
<div class="error-box">
<div class="error-title">? 错误报告</div>
<div class="error-time">{datetime.now().isoformat()}</div>
<div class="error-message">{error_info['message']}</div>
</div>
</body>
</html>
"""
# 2. 展示错误
canvas(action="present", html=html)
# 3. 截图
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
return screenshot
|
7. 批量截图
7.1 多页面截图
|
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
|
def batch_screenshot(urls):
"""批量截图多个页面"""
screenshots = []
for url in urls:
try:
# 加载页面
canvas(action="navigate", url=url)
time.sleep(2)
# 截图
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
screenshots.append({
"url": url,
"screenshot": screenshot,
"status": "success"
})
except Exception as e:
screenshots.append({
"url": url,
"error": str(e),
"status": "failed"
})
return screenshots
|
7.2 定时截图
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def scheduled_screenshot(url, interval_minutes=5, count=12):
"""定时截图"""
screenshots = []
for i in range(count):
canvas(action="navigate", url=url)
time.sleep(2)
screenshot = canvas(action="snapshot", outputFormat="png")
screenshots.append({
"time": datetime.now().isoformat(),
"screenshot": screenshot
})
if i < count - 1:
time.sleep(interval_minutes * 60)
return screenshots
|
8. 图像处理
8.1 调整尺寸
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def resize_screenshot(screenshot, max_width=800):
"""调整截图尺寸"""
# 使用PIL处理图像
from PIL import Image
import io
import base64
# 解码图像
if isinstance(screenshot, dict):
img_data = base64.b64decode(screenshot['data'])
else:
img_data = screenshot
img = Image.open(io.BytesIO(img_data))
# 调整尺寸
if img.width > max_width:
ratio = max_width / img.width
new_height = int(img.height * ratio)
img = img.resize((max_width, new_height))
# 返回处理后的图像
output = io.BytesIO()
img.save(output, format='PNG')
return output.getvalue()
|
8.2 添加水印
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
def add_watermark(screenshot, watermark_text):
"""添加水印"""
from PIL import Image, ImageDraw, ImageFont
import io
img = Image.open(io.BytesIO(screenshot))
draw = ImageDraw.Draw(img)
# 添加水印文字
draw.text(
(10, 10),
watermark_text,
fill=(128, 128, 128, 128)
)
output = io.BytesIO()
img.save(output, format='PNG')
return output.getvalue()
|
9. 截图存储
9.1 本地存储
|
1
2
3
4
5
6
7
8
|
def save_to_local(screenshot, filename):
"""保存到本地"""
with open(filename, 'wb') as f:
if isinstance(screenshot, dict):
import base64
f.write(base64.b64decode(screenshot['data']))
else:
f.write(screenshot)
|
9.2 云存储
|
1
2
3
4
5
|
def upload_to_cloud(screenshot, filename):
"""上传到云存储"""
# 使用uploader技能
# 或直接调用云存储API
pass
|
10. 最佳实践
10.1 截图优化
| 优化项 |
说明 |
| 等待渲染 |
确保内容加载完成 |
| 合适尺寸 |
根据用途选择尺寸 |
| 格式选择 |
PNG质量高,JPG文件小 |
| 压缩优化 |
适当压缩减少文件大小 |
10.2 常见问题
| 问题 |
解决方案 |
| 截图空白 |
增加等待时间 |
| 内容不完整 |
使用全页面截图 |
| 图像模糊 |
提高分辨率 |
| 文件过大 |
使用JPG格式 |
11. 总结
核心要点
| 要点 |
说明 |
| snapshot |
截图操作 |
| outputFormat |
输出格式 |
| width/height |
截图尺寸 |
| 等待渲染 |
确保内容加载 |
|