shot-scraper 1.10 新增视频演示录制

Simon Willison··作者 Simon Willison

关键信息

这次发布以 YAML 形式的 storyboard 为核心,可以定义要启动的服务器、目标 URL、视口大小、是否显示光标、等待条件以及脚本场景。示例还展示了通过 JSON cookie 文件提供可选认证支持,并可用 JavaScript 钩子自定义浏览器行为,例如处理剪贴板。

资讯摘要

Simon Willison 表示,shot-scraper 1.10 现在加入了一个新的 `shot-scraper video` 命令。这个命令接受一个 `storyboard.yml` 文件,用来描述要在网页应用上执行的流程,并借助 Playwright 将该流程录制成视频。Willison 将这个功能视为他长期尝试的一部分,目标是帮助编程智能体产出能够证明“确实可工作”的演示。为了展示效果,他给出了一个视频示例,演示对象是 Datasette 中一个仍在开发中的功能:允许用户把粘贴的 CSV、TSV 或 JSON 数据创建为新表。

这个演示通过一个命令生成,命令中指定了 storyboard 文件、通过 `--auth` 提供认证信息,并要求输出为 MP4。文中还说明,认证文件里包含一个 cookie,相关用法在项目文档中已有介绍。示例 storyboard 还定义了要启动的服务器参数、本地访问地址、1280×720 的视口、显示鼠标指针、等待条件,以及一段自定义 JavaScript,用来覆盖 `navigator.clipboard` 以适配演示场景。整篇文章把这次发布描述为一种实用的新方式,可用于自动化并录制浏览器工作流,从而帮助文档展示和功能验证。

shot-scraper 1.10 新增视频演示录制

资讯正文

shot-scraper video 是今天发布的 shot-scraper 1.10 版本中新加入的命令,它接受一个定义了要针对 Web 应用运行的一系列步骤的 storyboard.yml 文件,并使用 Playwright 录制这套流程的视频。我之前写过关于让编码代理产出工作演示的重要性;这是我为实现这一点所做的最新尝试。

下面是一个使用 shot-scraper video 创建的示例视频,它演示了 Datasette 中一项仍在开发中的功能:可以从粘贴的 CSV、TSV 或 JSON 数据创建新表:

那个视频是通过运行下面这条命令创建的:

(这个 --auth JSON 文件包含一个 cookie,正如文档中这里所描述的那样。)

下面是 datasette-bulk-insert-storyboard.yml 文件:

<span class="pl-s"> Object.defineProperty(navigator, "clipboard", {</span>

<span class="pl-s"> configurable: true,</span>

<span class="pl-s"> get: () =&gt; ({</span>

<span class="pl-s"> writeText: async (text) =&gt; {</span>

<span class="pl-s"> clipboardText = String(text);</span>

<span class="pl-s"> },</span>

<span class="pl-s"> readText: async () =&gt; clipboardText,</span>

<span class="pl-s"> }),</span>

<span class="pl-s"> });</span>

<span class="pl-s"> })();</span>

<span class="pl-s"></span><span class="pl-ent">scenes</span>:

- <span class="pl-ent">name</span>: 批量插入已有表格行

<span class="pl-ent">do</span>:

- <span class="pl-ent">pause</span>: <span class="pl-c1">0.8</span>

- <span class="pl-ent">click</span>: <span class="pl-s"><span class="pl-pds">'</span>button[data-table-action="insert-row"]<span class="pl-pds">'</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>#row-edit-dialog[open]<span class="pl-pds">"</span></span>

- <span class="pl-ent">pause</span>: <span class="pl-c1">0.5</span>

- <span class="pl-ent">click</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-bulk-insert<span class="pl-pds">"</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-bulk-textarea<span class="pl-pds">"</span></span>

- <span class="pl-ent">pause</span>: <span class="pl-c1">0.5</span>

- <span class="pl-ent">click</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-copy-template<span class="pl-pds">"</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>text=Copied<span class="pl-pds">"</span></span>

- <span class="pl-ent">pause</span>: <span class="pl-c1">0.8</span>

- <span class="pl-ent">fill</span>:

<span class="pl-ent">into</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-bulk-textarea<span class="pl-pds">"</span></span>

<span class="pl-ent">text</span>: <span class="pl-s">|

title,owner,status,priority,notes

Prepare release video,Ana,doing,1,Recorded with shot-scraper

Check pasted CSV import,Ben,review,3,Previewed before inserting

Share the branch demo,Chen,queued,2,Bulk insert creates three rows

</span> - <span class="pl-ent">pause</span>: <span class="pl-c1">0.8</span>

- <span class="pl-ent">click</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-save<span class="pl-pds">"</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>text=Previewing 3 rows.<span class="pl-pds">"</span></span>

- <span class="pl-ent">pause</span>: <span class="pl-c1">1.2</span>

- <span class="pl-ent">click</span>: <span class="pl-s"><span class="pl-pds">"</span>.row-edit-save<span class="pl-pds">"</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>text=3 rows inserted.<span class="pl-pds">"</span></span>

- 暂停:1.0

- 点击:“.row-edit-cancel”

- 等待:“text=Prepare release video”

- 暂停:1.0

- 名称:从粘贴的 CSV 创建表

打开:http://127.0.0.1:6419/demo

等待:“details.actions-menu-links summary”

执行:

- 暂停:0.8

- 点击:“details.actions-menu-links summary”

- 点击:“button[data-database-action="create-table"]”

- 等待:“#table-create-dialog[open]”

- 暂停:0.5

- 填入:

到:“.table-create-table-name”

文本:“launch_metrics”

- 点击:“.table-create-from-data”

- 等待:“.table-create-data-textarea”

- 暂停:0.5

- 填入:

到:“.table-create-data-textarea”

文本:|

metric_id,name,score,recorded_on

m001,Activation rate,87.5,2026-06-29

m002,Retention check,72.25,2026-06-30

m003,CSV import health,95,2026-07-01

- 暂停:0.8

- 点击:“.table-create-save”

- 等待:“text=Previewing 3 rows.”

- 暂停:1.2

- 点击:“.table-create-save”

- <span class="pl-ent">wait_for_url</span>: <span class="pl-s"><span class="pl-pds">"</span>**/demo/launch_metrics<span class="pl-pds">"</span></span>

- <span class="pl-ent">wait_for</span>: <span class="pl-s"><span class="pl-pds">"</span>text=Activation rate<span class="pl-pds">"</span></span>

- <span class="pl-ent">pause</span>: <span class="pl-c1">1.2</span></pre></div>

视频命令文档里有更简单的示例,不过就这篇文章而言,我想还是用一个更完整的例子。

那个演示用的 YAML 故事板完全是由在 Codex Desktop 中运行的 GPT-5.5 xhigh 构建的,使用的是我在 <code>~/dev/datasette</code> 检出目录里、针对<a href="https://github.com/simonw/datasette/commits/b759ea548606bc9bf9a4bf0e33e2d57ead7e0ab8/">这个分支</a>运行的以下提示词:

<blockquote>

<p><code>Review the changes on this branch.</code></p>

<p><code>cd to ~/dev/shot-scraper and run the command "uv run shot-scraper video --help"</code></p>

<p><code>Now use that new video command to record a video demo of the new features from this branch, including running a "uv run datasette -p 6419 --root --secret 1 /tmp/demo.db" development server so you can record the video against a demo DB that you first create.</code></p>

</blockquote>

现在我已经发布了这个功能,这个提示词也可以改成“<code>run uvx shot-scraper video --help</code>”,而且应该能得到同样的结果。

我非常喜欢这种模式:一个命令的 <code>--help</code> 输出提供了足够详细的信息,让编码代理可以直接使用它——这在某种程度上就像把一个 <code>SKILL.md</code> 文件直接打包进工具里。我在 <a href="https://simonwillison.net/2026/Feb/10/showboat-and-rodney/">showboat 和 rodney</a> 里也用了同样的模式。

<h4 id="how-i-built-this">我是怎么构建这个的</h4>

<code>shot-scraper video</code> 最初是一个实验性原型。<code>shot-scraper</code> 构建在 <a href="https://playwright.dev/">Playwright</a> 之上,它需要的关键功能是让 Playwright 能够录制浏览器会话视频,并且拥有足够的控制能力来制作想要的演示效果。

几年前我第一次尝试这个功能时,发现 Playwright 生成的视频里包含了额外的界面元素,这些内容对于调试测试失败很有用,但对于产品演示来说却不想要。

这个问题后来他们在一段时间前已经修复了,但仍然有一些小障碍。尤其是我遇到了<a href="https://github.com/simonw/shot-scraper/pull/194/changes/c2f3b3a52ba84f2adcf3ad6da4d39c2570328584#issuecomment-4724459369">视频开头的几个空白帧</a>,因为录制机制在浏览器加载第一个 URL 之前就已经启动了。

Playwright 1.59 新增了一种<a href="https://playwright.dev/python/docs/api/class-screencast">screencast 机制</a>,可以对视频录制提供更细粒度的控制。这几乎就是我需要的功能了,但生成的视频宽度固定为 800 像素。

我找到了一个<a href="https://github.com/microsoft/playwright/pull/41183">已合并、可修复该问题的 PR</a>,但它当时还没有包含在某个发布版里。直到昨天,他们才在 <a href="https://github.com/microsoft/playwright-python/releases/tag/v1.61.0">playwright-python 1.61.0</a> 中发布了它,我终于可以继续完成这个功能的实现了!

代码本身全都是由 Codex Desktop 里的 GPT-5.5 xhigh 编写的。我还让它写了文档,这给了我一个非常有用的框架来审查设计——这个功能的大量迭代都来自于审阅那份文档,发现其中哪些内容是多余的、不一致的或令人困惑的,然后要求(或直接指示)它给出一个更好的设计。

YAML 格式本身主要是由这个编码代理定义的。我让它<a href="https://github.com/simonw/shot-scraper/blob/1.10/shot_scraper/video.py#L24">使用 Pydantic</a>来同时定义和验证这个格式,部分原因是为了让设计更容易审查。

这很好地说明了这样一种功能:如果没有编码代理的支持,我几乎肯定不会接手去做。我在 2024 年 2 月提交了最初的<a href="https://github.com/simonw/shot-scraper/issues/142">问题单</a>,但在我所有其他项目之间,很难挤出完成这个工作的必要时间。

来源与参考

  1. 原始链接
  2. Have your agent record video demos of its work with shot-scraper video

收录于 2026-07-01