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

参考资料


相关推荐
进哥AI研习社17 小时前
WebGL 与视频背景——首屏视觉层的性能博弈
性能优化·webgl·gpu·canvas·shader·帧率·视频背景
AC赳赳老秦21 小时前
个保法下数据处理:OpenClaw 自动过滤公开数据中的个人信息,保障采集分析合规性
java·python·sqlite·json·php·deepseek·openclaw
海兰2 天前
【插件】Logbook 插件完全指南(适配 Ubuntu 24.04)
linux·运维·人工智能·ubuntu·agent·openclaw
AC赳赳老秦2 天前
企业标签体系搭建:基于 OpenClaw 采集的多维度公开数据,构建企业画像标签库
java·运维·服务器·python·信息可视化·deepseek·openclaw
海兰3 天前
【插件】OpenClaw 上下文引擎指南
人工智能·agent·openclaw
AC赳赳老秦3 天前
多源数据关联挖掘:OpenClaw 打通工商、招投标、专利公开数据,挖掘企业业务关联关系
java·c语言·javascript·c++·python·deepseek·openclaw
七夜zippoe4 天前
OpenClaw 边缘部署实战:IoT 场景下的轻量级 Agent 集群与离线自治
agent·集群·lot·轻量级·边缘部署·openclaw
MicrosoftReactor4 天前
技术速递|Canvas 如何让 Agentic Workflow 更可见、可控、更高效
ai·agent·canvas
梦想的颜色5 天前
Pi‑Agent 深度硬核解析:极简主义编程智能体,OpenClaw 底层内核源码剖析
大模型应用·ai 编程·codingagent·openclaw·piagent·agent 框架源码·2026ai 工具
AC赳赳老秦7 天前
语义采集进阶实战:利用 OpenClaw AI 语义识别自动提取网页核心信息,无需手动编写选择器
java·运维·服务器·python·信息可视化·deepseek·openclaw