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

参考资料


相关推荐
福兮说8 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
格数致用20 小时前
01 - Luckysheet电子表格技术原理与架构深度解析
canvas·luckysheet·企业级报表
福兮说1 天前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
AC赳赳老秦2 天前
OpenClaw 与 FineBI 联动方案:公开数据自动采集与实时业务分析看板实践
大数据·开发语言·python·php·finebi·deepseek·openclaw
dtsola3 天前
一个人怎么指挥一支 AI 队伍
人工智能·程序员·ai创业·独立开发者·openclaw·一人公司·小遥claw
七夜zippoe3 天前
Agent 自动化测试:怎么给“会自己决策“的程序写测试
ai·自动化·agent·测试·openclaw
AC赳赳老秦3 天前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
AC赳赳老秦4 天前
OpenClaw 数据引用规范自动生成:为公开数据构建可信来源标注与标准引用体系
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
AC赳赳老秦5 天前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw
miss7 天前
给规则引擎加一个拖拽画布:Vue Flow 单向投影 + 布局语义分离
javascript·vue.js·canvas