OpenClaw Canvas 截图:页面捕获与保存

目录

    • 摘要
    • [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:设备配对与管理

参考资料


相关推荐
QING6182 小时前
如何使用Compose 绘制提升性能 —— 新手指南
kotlin·android jetpack·canvas
无心水2 小时前
【Harness:落地实战】16、从“只会说”到“能干活”:OpenClaw落地,手动Harness的架构与实现深度解析
人工智能·架构·设计规范·openclaw·养龙虾·hermes·honcho
wengqidaifeng4 小时前
4. 拆解 tmtpost-news-daily:一个 OpenClaw 每日早报 Skill 如何落地
搜索引擎·ai·openclaw
wengqidaifeng4 小时前
5. OpenClaw 从学习到落地:企业路线图、个人学习路径与项目复盘
学习·ai·openclaw
wengqidaifeng1 天前
2. OpenClaw 架构落地指南:部署、渠道集成与安全边界全解
安全·ai·架构·openclaw
小脑斧1231 天前
自媒体内容工业化:基于AI Skills低代码实现穿搭账号矩阵自动化量产
人工智能·低代码·媒体·skills·openclaw·hermes·marvis
醇氧1 天前
【Hermes Agent】使用阿里云百炼 Token Plan(方式一:命令行一键配置)
阿里云·云计算·es·openclaw
羑悻的小杀马特1 天前
从 Claude Code 到 QClaw:AgentSkills 规范的跨生态实践与工程取舍!
人工智能·自动化·agent·skills·openclaw·qclaw
wengqidaifeng1 天前
3. OpenClaw Skill 开发方法论:从 weather-bit 到 qrcode-gen
ai·openclaw