目录
-
- 摘要
- [1. 引言 - 截图功能的价值](#1. 引言 - 截图功能的价值)
-
- [1.1 截图应用场景](#1.1 截图应用场景)
- [1.2 截图功能概览](#1.2 截图功能概览)
- [1.3 支持的输出格式](#1.3 支持的输出格式)
- [2. 基本截图操作](#2. 基本截图操作)
-
- [2.1 全页面截图](#2.1 全页面截图)
- [2.2 指定格式](#2.2 指定格式)
- [2.3 设置质量](#2.3 设置质量)
- [2.4 设置尺寸](#2.4 设置尺寸)
- [3. 截图参数详解](#3. 截图参数详解)
-
- [3.1 参数列表](#3.1 参数列表)
- [3.2 区域截图](#3.2 区域截图)
- [3.3 返回值](#3.3 返回值)
- [4. 实战案例一:报告截图](#4. 实战案例一:报告截图)
-
- [4.1 场景描述](#4.1 场景描述)
- [4.2 实现代码](#4.2 实现代码)
- [5. 实战案例二:网页存档](#5. 实战案例二:网页存档)
-
- [5.1 场景描述](#5.1 场景描述)
- [5.2 实现代码](#5.2 实现代码)
- [6. 实战案例三:错误记录](#6. 实战案例三:错误记录)
-
- [6.1 场景描述](#6.1 场景描述)
- [6.2 实现代码](#6.2 实现代码)
- [7. 批量截图](#7. 批量截图)
-
- [7.1 多页面截图](#7.1 多页面截图)
- [7.2 定时截图](#7.2 定时截图)
- [8. 图像处理](#8. 图像处理)
-
- [8.1 调整尺寸](#8.1 调整尺寸)
- [8.2 添加水印](#8.2 添加水印)
- [9. 截图存储](#9. 截图存储)
-
- [9.1 本地存储](#9.1 本地存储)
- [9.2 云存储](#9.2 云存储)
- [10. 最佳实践](#10. 最佳实践)
-
- [10.1 截图优化](#10.1 截图优化)
- [10.2 常见问题](#10.2 常见问题)
- [11. 总结](#11. 总结)
-
- [11.1 核心要点](#11.1 核心要点)
- [11.2 下一步](#11.2 下一步)
- 参考资料
摘要
本文详细介绍 OpenClaw Canvas 的截图功能。从基本截图、全页面捕获、元素截图到图像处理,全面解析如何通过 Canvas 实现灵活的页面捕获。通过实际案例演示报告生成、内容存档、错误记录等场景,帮助开发者掌握 Canvas 截图的实际应用。📸
1. 引言 - 截图功能的价值
1.1 截图应用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 报告生成 | 生成可视化报告 | 数据报告截图 |
| 内容存档 | 保存页面内容 | 网页快照 |
| 错误记录 | 记录错误状态 | Bug截图 |
| 分享展示 | 分享界面内容 | 成果展示 |
| 自动化测试 | 测试结果验证 | UI测试截图 |
1.2 截图功能概览
#mermaid-svg-Bg1f84YOgOYt0IK3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bg1f84YOgOYt0IK3 .error-icon{fill:#552222;}#mermaid-svg-Bg1f84YOgOYt0IK3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bg1f84YOgOYt0IK3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .marker.cross{stroke:#333333;}#mermaid-svg-Bg1f84YOgOYt0IK3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bg1f84YOgOYt0IK3 p{margin:0;}#mermaid-svg-Bg1f84YOgOYt0IK3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster-label text{fill:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster-label span{color:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster-label span p{background-color:transparent;}#mermaid-svg-Bg1f84YOgOYt0IK3 .label text,#mermaid-svg-Bg1f84YOgOYt0IK3 span{fill:#333;color:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .node rect,#mermaid-svg-Bg1f84YOgOYt0IK3 .node circle,#mermaid-svg-Bg1f84YOgOYt0IK3 .node ellipse,#mermaid-svg-Bg1f84YOgOYt0IK3 .node polygon,#mermaid-svg-Bg1f84YOgOYt0IK3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .rough-node .label text,#mermaid-svg-Bg1f84YOgOYt0IK3 .node .label text,#mermaid-svg-Bg1f84YOgOYt0IK3 .image-shape .label,#mermaid-svg-Bg1f84YOgOYt0IK3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bg1f84YOgOYt0IK3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .rough-node .label,#mermaid-svg-Bg1f84YOgOYt0IK3 .node .label,#mermaid-svg-Bg1f84YOgOYt0IK3 .image-shape .label,#mermaid-svg-Bg1f84YOgOYt0IK3 .icon-shape .label{text-align:center;}#mermaid-svg-Bg1f84YOgOYt0IK3 .node.clickable{cursor:pointer;}#mermaid-svg-Bg1f84YOgOYt0IK3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .arrowheadPath{fill:#333333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bg1f84YOgOYt0IK3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bg1f84YOgOYt0IK3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bg1f84YOgOYt0IK3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster text{fill:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 .cluster span{color:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bg1f84YOgOYt0IK3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bg1f84YOgOYt0IK3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bg1f84YOgOYt0IK3 .icon-shape,#mermaid-svg-Bg1f84YOgOYt0IK3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bg1f84YOgOYt0IK3 .icon-shape p,#mermaid-svg-Bg1f84YOgOYt0IK3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bg1f84YOgOYt0IK3 .icon-shape .label rect,#mermaid-svg-Bg1f84YOgOYt0IK3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bg1f84YOgOYt0IK3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bg1f84YOgOYt0IK3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bg1f84YOgOYt0IK3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} snapshot
截图类型
全页面截图
可视区域截图
元素截图
PNG/JPG
返回图像数据
1.3 支持的输出格式
| 格式 | 说明 | 适用场景 |
|---|---|---|
| PNG | 无损压缩 | 需要高质量 |
| JPG/JPEG | 有损压缩 | 文件较小 |
2. 基本截图操作
2.1 全页面截图
python
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
2.2 指定格式
python
# PNG格式
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
# JPG格式
screenshot = canvas(
action="snapshot",
outputFormat="jpg"
)
2.3 设置质量
python
screenshot = canvas(
action="snapshot",
outputFormat="jpg",
quality=80 # 1-100
)
2.4 设置尺寸
python
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 区域截图
python
# 截取指定区域
screenshot = canvas(
action="snapshot",
x=100,
y=100,
width=800,
height=600
)
3.3 返回值
截图返回的是图像数据,可以直接保存或处理。
python
# 返回格式示例
{
"type": "image",
"format": "png",
"data": "base64编码的图像数据"
}
4. 实战案例一:报告截图
4.1 场景描述
生成数据报告并截图保存。
4.2 实现代码
python
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 实现代码
python
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 实现代码
python
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 多页面截图
python
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 定时截图
python
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 调整尺寸
python
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 添加水印
python
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 本地存储
python
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 云存储
python
def upload_to_cloud(screenshot, filename):
"""上传到云存储"""
# 使用uploader技能
# 或直接调用云存储API
pass
10. 最佳实践
10.1 截图优化
| 优化项 | 说明 |
|---|---|
| 等待渲染 | 确保内容加载完成 |
| 合适尺寸 | 根据用途选择尺寸 |
| 格式选择 | PNG质量高,JPG文件小 |
| 压缩优化 | 适当压缩减少文件大小 |
10.2 常见问题
| 问题 | 解决方案 |
|---|---|
| 截图空白 | 增加等待时间 |
| 内容不完整 | 使用全页面截图 |
| 图像模糊 | 提高分辨率 |
| 文件过大 | 使用JPG格式 |
11. 总结
11.1 核心要点
| 要点 | 说明 |
|---|---|
| snapshot | 截图操作 |
| outputFormat | 输出格式 |
| width/height | 截图尺寸 |
| 等待渲染 | 确保内容加载 |
11.2 下一步
- 第55篇:OpenClaw Canvas A2UI:交互式界面开发
- 第56篇:OpenClaw Nodes:设备配对与管理