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

参考资料


相关推荐
福兮说14 小时前
纯前端把图片压缩到指定体积:canvas.toBlob 配合二分查找
前端·javascript·canvas·图片处理
宇文仲竹14 小时前
docker agent 代替 openclaw 的 本地 简化 agent
openclaw
AI工具人PM产品经理6 天前
PIL 脚本批量生成小程序图标实战:零美工做出全套 tabBar
微信小程序·canvas·pil·python3.11·工具脚本
AC赳赳老秦7 天前
文旅市场公开数据分析:基于 OpenClaw 采集景区客流与门票公示数据,生成区域文旅热度监测报告
java·c语言·python·php·symfony·deepseek·openclaw
AC赳赳老秦8 天前
环保监测公开数据应用:OpenClaw 抓取空气与水质公开监测数据,开展区域环境质量趋势分析
大数据·数据库·人工智能·python·php·deepseek·openclaw
Blockbuater_drug8 天前
MCP Server 接入实战: 9种平台配置差异与凭证安全
claude·cursor·mcp·openclaw·hermes agent·dsh·agent 配置
2601_962298938 天前
阿里云计算巢部署 OpenClaw 保姆级图文攻略|Slack集成+千问Qwen3.6-Plus配置+新手避坑教程
阿里云·新手教程·openclaw·slack集成·千问qwen3.6-plus
小北的AI科技分享9 天前
独立站商品如何进入AI搜索推荐:GEO优化全攻略
引用·页面·权威
AC赳赳老秦9 天前
农产品公开数据应用:OpenClaw 抓取农产品价格、产销公开数据,实现农产品行情动态监测
java·c语言·javascript·python·php·deepseek·openclaw
又吹风_Bassy9 天前
WhatsCanvas教程-第十一章:性能优化
性能优化·canvas·skia·2d渲染库·nanovg·whatscanvas·2d图形库