今天早上,我完全在手机上工作,抓取了一个会议网站,并使用 OpenAI Codex 和 Claude Artifacts 的组合编写了一个用于与日程表交互的替代 UI。
本周末是Open Sauce 2025 ,这是湾区面向科学和工程领域 YouTube 创作者的第三届大会。我有几个朋友要去,他们抱怨官方日程在手机上很难浏览——甚至没有手机主页的链接,而且即使找到议程,也不太方便在移动设备上查看。
今天早上我们出去喝咖啡,所以我只带了手机,但我决定看看是否可以修好它。
TLDR:完全在我的 iPhone 上工作,结合使用 ChatGPT 移动应用程序中的OpenAI Codex和通过 Claude 应用程序的 Claude Artifacts,我能够抓取完整的时间表,然后构建和部署它: tools.simonwillison.net/open-sauce-2025
该网站提供了更快的加载速度和更实用的议程视图,但更重要的是,它包含一个“下载日历(ICS)”选项,允许手机用户(Android 和 iOS)轻松地将日程事件直接导入他们选择的日历应用程序中。
以下是关于我如何构建它的一些详细说明。
取消时间表
第一步是将日程安排转化为结构化的格式。我的 iPhone 上没有好用的查看源代码的工具,所以我采用了另一种方法,将日程安排网站转化为结构化数据。
我的第一个想法是将时间表的屏幕截图放在手机上,然后将图像转储到 LLM 视觉文件中 – 但是时间表太长了,我不想滚动浏览几个不同的页面并将几十张图像拼接在一起。
如果我在笔记本电脑上工作,我会转向抓取:我会在网站本身中挖掘并找出数据的来源,然后编写代码将其提取出来。
我怎样才能在手机上做同样的事情?
我决定使用OpenAI Codex –托管工具,而不是名称令人困惑的CLI 实用程序。
Codex 最近增加了在尝试解决任务时与互联网交互的功能。我有一个专门针对 GitHub 仓库配置的 Codex“环境”,它不做任何其他事情,纯粹是为了在那里运行支持互联网的会话,从而执行任意支持网络的命令。
我在那里启动了一个新任务(使用 ChatGPT iPhone 应用程序内的 Codex 界面)并提示:
Install playwright and use it to visit https://opensauce.com/agenda/ and grab the full details of all three day schedules from the tabs - Friday and Saturday and Sunday - then save and on Data in as much detail as possible in a JSON file and submit that as a PR
Codex 令人沮丧的地方在于你只有一次机会:它可以离开并自主地长时间处理一项任务,但在它工作期间你无法给它后续的提示。你可以等它完全完成,然后让它在新的会话中重试,但理想情况下,你给它的指令足以让它进入完成状态,并向你的代码库提交一个包含结果的拉取请求。
我很幸运:上述提示完全按照预期进行。
Codex 运行了13 分钟!我当时正坐在咖啡店里聊天,偶尔查看一下日志,看看它在干什么。
它尝试了各种各样的方法,都涉及运行 Playwright Python 库来与网站交互。您可以在此处查看完整的记录。其中包括“ xxd 似乎未安装。我将使用“vim-common”或“xxd”来修复它。 ”之类的注释。
最终,它下载了一个名为schedule-overview-main-1752724893152.js (316KB)的巨大的混淆JavaScript块,然后对其运行了一系列复杂的grep、grep、sed、strings、xxd和dd命令,以找出原始计划数据的位置,从而将其提取出来。
这是它最终编写的extract_schedule.py Python 脚本,该脚本使用 Playwright 保存该schedule-overview-main-1752724893152.js
文件,然后使用以下代码提取原始数据(它在 Python 内部调用 Node.js,没有明显的原因):
节点脚本= ( “const fs = require('fs');” f“const d = fs.readFileSync(' { tmp_path } ','utf8');” “const m=d.match(/var oo=( \\ {.*? \\ });/s);” “如果(!m){抛出新的错误('未找到');}” “const obj = eval('(' + m[1] + ')');” f“fs.writeFileSync(' { OUTPUT_FILE } ',JSON.stringify(obj,null,2));” ) 子进程.运行([ 'node' , '-e' , node_script ], check = True )
按照指示,它随后向我的代码库提交了一个 PR 。它包含 Python Playwright 脚本,但更重要的是,它还包含完整提取的Schedule.json文件。这意味着我现在拥有了日程安排数据,以及一个带有开放 CORS 标头的raw.githubusercontent.com
URL,可以被 Web 应用获取!
构建 Web 应用程序
现在我有了数据,下一步就是构建一个 Web 应用程序来预览它并以更有用的格式提供它。
我决定我想要两样东西:一个用于浏览日程表的美观的移动友好界面,以及将该日程表导入日历应用程序(例如 Apple 或 Google 日历)的机制。
我尝试了好几次才成功。最大的挑战是将那 63KB 的 JSON 时刻表数据导入到应用中。我尝试了好几种方法,一次是在咖啡店里,一次是在开车送朋友去最近的 BART 车站时,用 iPhone 做的。
- 使用 ChatGPT Canvas 和 o3,因为与 Claude Artifacts 不同,如果你将某个域名列入白名单,Canvas 可以从远程 URL 获取数据。后来我发现,在笔记本电脑上查看时,这种方法有效,但在手机上却报错,所以我放弃了。
- 我将 JSON 文件上传到 Claude,并让它构建一个直接读取文件的构件,结果却失败了,错误信息为“undefined is not an object (evaluating ‘window.fs.readFile’)”。Claude 4 的系统提示符让我以为这个方法可以成功,但我不知道为什么它没有成功。
- 让 Claude 将完整的 JSON 复制到工件中。这花了太长时间——输入 63KB 的 JSON 数据对 LLM 令牌的使用来说并不合理,而且当我在隧道里开车时网络连接断断续续时,它就崩溃了。
- 告诉 Claude 改为从该调度的 URL 获取 JSON 数据。这是我最后的办法,因为 Claude Artifacts UI 阻止了对外部 URL 的访问,所以你必须将代码复制粘贴到一个单独的界面(在 iPhone 上,它仍然没有“全选”按钮),整个过程非常令人沮丧。
最终方案成功了!以下是我和 Claude 一起完成的一系列操作,最终实现了一个可行的方案——完整记录如下:
Use your analyst tool to read this JSON file and show me the top level keys
这是为 Claude 做准备 – 我想提醒它关于它的window.fs.readFile
函数并让它读取足够多的 JSON 来理解结构。
Build an artifact with no react that turns the schedule into a nice mobile friendly webpage - there are three days Friday, Saturday and Sunday, which corresponded to the 25th and 26th and 27th of July 2025
Don't copy the raw JSON over to the artifact - use your fs function to read it instead
Also include a button to download ICS at the top of the page which downloads a ICS version of the schedule
我注意到日程表数据有“星期五”、“星期六”和“星期日”的键,但没有日期信息,所以我输入了这些键。后来才发现我搞错了!
这导致我得到的页面版本出现错误,因为fs.readFile()
由于某种原因无法从 artifact 加载数据。因此我修复了这个问题:
Change it so instead of using the readFile thing it fetches the same JSON from https://raw.githubusercontent.com/simonw/.github/f671bf57f7c20a4a7a5b0642837811e37c557499/schedule.json
…然后将 HTML 复制到 Gist 并使用gistpreview.github.io进行预览 – 这是预览。
然后我们抽查了一下,因为出错的可能性实在太大了。幸好,课程表的 JSON 本身从未经过 LLM 的往返传输,所以我们不用担心幻听的会话细节。但这几乎是纯粹的氛围编码,所以有很大的风险会出错。
我给自己定了一个截止日期,就是“把朋友送到 BART 车站之前”,最后我差几秒就完成了。我使用 GitHub 移动 Web 界面将生成的 HTML 粘贴到我的 simonw/tools GitHub 仓库中,并将其部署到最终的tools.simonwillison.net/open-sauce-2025网址。
…然后我们注意到我们错过了一个错误:我给它的日期是“2025 年 7 月 25 日、26 日和 27 日”,但实际上那晚了一周,正确的日期是 7 月 18 日至 20 日。
值得庆幸的是,我已经针对我的simonw/tools
仓库配置了 Codex,因此修复该问题只需要使用以下命令启动一个新的 Codex 会话:
The open sauce schedule got the dates wrong - Friday is 18 July 2025 and Saturday is 19 and Sunday is 20 - fix it
这是Codex 的记录,我通过它获得了这个 PR并进行了部署,同样使用了 GitHub 移动网络界面。
这一切表明
所以,总结一下:我能够抓取一个网站(甚至不需要查看源代码),将生成的 JSON 数据转换成适合移动设备的网站,添加 ICS 导出功能,并将结果部署到完全在手机上运行的静态托管平台(GitHub Pages)。
如果我有一台笔记本电脑,这个项目就会更快,但老实说,除了稍微多一点动手调试之外,我不会用特别不同的方式来做它。
我可以同时做其他事情 – Codex 抓取项目完全自主运行,应用程序构建本身更加复杂,因为我必须解决我所使用的工具在从外部来源获取数据方面的限制。
和往常一样,我之前 25 年以上的 Web 开发经验对于项目的实施至关重要。我了解 Codex、Artifacts、GitHub、Playwright、CORS 标头、Artifacts 沙盒限制以及手机上 ICS 文件的功能。
整个过程太有趣了!能够直接用手机启动多个编程代理,让它们解决相当复杂的问题,而且只需关注部分细节,这充分证明了为什么我一直热衷于探索人工智能辅助编程的前沿。
标签: github 、 icalendar 、 scraping 、 ai 、 playwright 、 openai 、 generative-ai 、 chatgpt 、 llms 、 ai-assisted-programming 、 claude 、 claude-artifacts 、 ai-agents 、 vibe-coding 、 coding-agents
原文: https://simonwillison.net/2025/Jul/17/vibe-scraping/#atom-everything