手机上用Claude Code搭建iNaturalist数据展示工具全流程

Simon Willison用手机和Claude Code构建了零服务器的iNaturalist数据展示工具
Simon Willison在露营中完全使用手机和Claude Code for web,构建了一个展示iNaturalist自然观察记录的工具。项目采用三层无服务器架构:Python CLI进行时空聚合数据处理,GitHub Actions定时抓取数据(Git Scraping模式),以及通过一条精确提示词生成的纯静态前端页面。整个系统零运维成本,利用GitHub原始文件的CORS支持实现免费数据API托管。
项目背景
Simon Willison(知名开发者、Datasette创始人)在一次露营旅行中,完全使用手机和Claude Code for web,搭建了一个展示iNaturalist自然观察记录的工具。这个项目展示了AI辅助编程在移动端的实际应用能力,也体现了一种轻量级但完整的数据管道设计思路。
Simon Willison在开发者社区中以推动数据工具民主化著称。他创建的Datasette是一个用于探索和发布数据的开源工具,核心理念是将SQLite数据库直接转化为可交互的Web API和界面。Datasette基于Python和SQLite构建,能将任意CSV、JSON甚至地理数据文件转化为带有SQL查询界面和JSON API的Web应用。SQLite作为嵌入式数据库,不需要独立的服务器进程,整个数据库就是一个单独的文件,这使得Datasette可以实现"零配置部署"。Simon围绕Datasette构建了一个庞大的插件生态系统(超过100个插件),涵盖数据可视化、地理信息展示、全文搜索等功能,还创建了sqlite-utils等配套工具,形成了一套完整的"SQLite生态工具链"。这种"数据即产品"的思维方式和对简单性、可移植性、透明性的追求贯穿了他的许多项目,本次的iNaturalist工具也不例外。
iNaturalist是一个全球性的自然观察社区平台,由加州科学院和国家地理学会联合运营,拥有超过200万注册用户和1.5亿条观察记录。用户可以上传动植物照片,平台通过计算机视觉模型(基于深度学习的图像分类)提供初步的物种识别建议,再由社区专家进行验证。iNaturalist提供了完善的RESTful API,支持按用户、地理范围、物种分类等多维度查询观察数据,返回包含GPS坐标、时间戳、物种分类信息和多分辨率图片URL的结构化JSON数据。具体来说,其v1 API基于Elasticsearch构建,支持复杂的地理空间查询(如bounding box和radius搜索),单次请求最多返回200条记录并支持分页游标。API返回的物种分类数据遵循生物学标准的分类层级(界、门、纲、目、科、属、种),每条观察记录还包含"研究级别"(Research Grade)标识——只有经过社区多人确认的物种鉴定才会获得此标识。值得一提的是,iNaturalist的数据还被整合进GBIF(全球生物多样性信息设施),成为全球生态研究的重要数据源之一。正是这套开放API为Simon的项目提供了数据基础。Simon拥有两个独立的iNaturalist账户,他希望将两个账户的观察记录按时间和地点聚合展示在同一个页面上。
技术架构:三层数据管道
整个项目由三个部分组成,形成了一条从数据获取到前端展示的完整管道。
Python CLI工具:数据获取与聚合
Simon首先构建了一个名为inaturalist-clumper的Python命令行工具,用于从iNaturalist API获取观察数据并进行"聚合"(clumping)处理。默认的聚合规则是:将时间间隔在2小时以内、地理距离在5公里以内的观察记录归为同一组。
这种时空聚合逻辑在地理信息系统(GIS)领域有着广泛的应用基础。从算法角度看,它本质上是一种基于密度的聚类方法,与经典的DBSCAN算法思路相似——通过定义"邻域"的范围(这里是时间窗口2小时和空间半径5公里)来判断哪些数据点属于同一簇。不过,Simon的方法与标准DBSCAN存在一个关键区别:DBSCAN需要同时满足密度阈值(minPts参数,即邻域内的最少点数),而Simon的方法更接近单链接层次聚类——只要新记录与已有簇中的任一记录满足时空邻近条件即可归入该簇。这种简化在数据量不大且分布稀疏的场景下非常有效,避免了DBSCAN在参数调优上的复杂性。
地理距离的计算通常采用Haversine公式,该公式考虑了地球的球面几何特征,能够根据两点的经纬度准确计算出它们之间的大圆距离。其核心数学表达式为 d = 2r·arcsin(√(sin²(Δφ/2) + cos(φ₁)·cos(φ₂)·sin²(Δλ/2))),其中r为地球半径(约6371公里),φ为纬度,λ为经度。Simon选择的5公里阈值和2小时时间窗口,恰好匹配了一次典型的徒步观察活动的时空范围——这种将领域知识编码为算法参数的做法,是数据工程中非常重要的设计决策。
这种聚合逻辑非常实用——它本质上代表了"一次外出观察"的概念,将零散的单条记录组织成有意义的活动单元。
Git Scraping自动化:用GitHub Actions定时更新数据
第二步是设置一个Git scraping仓库(simonw/inaturalist-clumps),通过GitHub Actions定期运行上述CLI工具,并将结果保存为clumps.json文件。
Git Scraping是Simon在2020年提出并推广的一种技术模式,其核心思想出人意料地简单:利用GitHub Actions的定时触发功能(基于cron表达式调度),周期性地运行脚本抓取外部数据,然后通过git commit和git push将结果提交到仓库。GitHub Actions的底层运行机制是:每次触发时,GitHub会在Azure云基础设施上启动一个全新的虚拟机(runner),提供2核CPU、7GB内存和14GB SSD存储的标准Linux环境。cron调度的最小精度为5分钟,但实际触发时间可能有数分钟的延迟,因为GitHub会对高负载时段的定时任务进行排队。对于Git Scraping场景,一个典型的工作流YAML文件只需要十几行配置:定义触发条件(schedule + workflow_dispatch手动触发)、设置Python环境、安装依赖、运行脚本、提交并推送变更。GitHub Actions为每个免费账户提供每月2000分钟的运行时间,对于这类轻量级的数据抓取任务绑绑有余。
每次数据更新都会产生一个新的Git提交,这意味着你不仅拥有最新的数据快照,还自动获得了完整的数据变更历史——何时新增了观察记录、何时数据发生了修改,都可以通过git log和git diff追溯。
更关键的是,GitHub上的原始文件(通过raw.githubusercontent.com域名访问)天然支持CORS(跨域资源共享)跨域访问,可以直接被前端JavaScript获取。CORS是浏览器的一种安全机制,默认情况下,网页中的JavaScript只能请求与当前页面同源的资源。如果要访问其他域名的数据,目标服务器必须在HTTP响应头中明确声明允许跨域访问(通过Access-Control-Allow-Origin头)。GitHub的原始文件服务默认设置了Access-Control-Allow-Origin: *,这意味着任何网页都可以直接通过fetch()请求获取仓库中的文件内容,无需搭建中间代理服务器。这一特性使得GitHub仓库实质上成为了一个免费的、带版本控制的静态数据API。
前端展示页面:一条提示词完成开发
最后一步是生成前端页面。Simon使用的是Claude Code for web——这是Anthropic推出的基于浏览器的AI编程环境,与需要在终端中运行的Claude Code CLI版本不同,它完全在浏览器中运行,用户通过自然语言描述需求,AI直接生成可预览的Web应用。两种形态在技术能力上有显著差异:Claude Code CLI能直接访问本地文件系统、执行shell命令、运行测试套件,适合复杂的多文件项目开发和重构;而Claude Code for web运行在浏览器沙箱中,生成的代码在隔离的WebContainer或iframe环境中执行,支持实时预览但无法访问本地文件系统。Web版本更适合快速原型开发和独立的单页应用构建,这恰好匹配了Simon在手机上的使用场景。两者共享相同的底层模型能力,但交互范式和适用场景有显著差异。这种基于浏览器的形态意味着它不依赖本地开发环境的配置,在手机浏览器上同样可以使用,这正是Simon能在露营途中用手机完成开发的技术前提。
Simon对Claude Code发出了一条精确的提示词,要求构建一个HTML应用:
- 从GitHub获取JSON数据
- 使用iNaturalist的small.jpg缩略图展示所有观察记录
- 支持
loading=lazy懒加载 - 点击缩略图时在模态框中显示large.jpg大图
- 同时显示物种的常见名称
其中,loading=lazy是HTML规范中的原生懒加载属性,于2019年在Chrome 76中首次实现,随后被Firefox、Edge和Safari陆续支持。其工作原理是浏览器通过Intersection Observer API监测图片元素是否进入视口(viewport)附近的区域,只有当图片即将可见时才发起HTTP请求下载图片资源。相比传统的JavaScript懒加载库(如lazysizes),原生实现的优势在于零JavaScript开销和更早的加载时机判断。对于Simon的项目来说,这一属性至关重要——iNaturalist的观察记录可能包含数百张图片,如果全部同时加载,不仅会消耗大量带宽,还会严重影响手机端的页面渲染性能。
一条提示词就完成了整个前端页面的开发,这体现了Claude Code在处理明确需求时的高效性。
设计亮点分析
零成本无服务器架构
整个系统不需要任何后端服务器运行。数据通过GitHub Actions定时更新,静态JSON文件托管在GitHub上,前端是纯静态HTML/JS页面。这意味着零运维成本、零托管费用,同时具备极高的可靠性。
这种架构理念与近年来兴起的Jamstack(JavaScript + API + Markup)思想高度一致。Jamstack的核心主张是:将动态功能从运行时转移到构建时,通过预生成静态资源和调用第三方API来替代传统的服务器端渲染。该术语由Netlify CEO Mathias Biilmann在2015年提出,最初写作JAMstack,早期的代表性工具包括Jekyll、Hugo等静态站点生成器,后来演进出Next.js、Nuxt.js等支持增量静态再生(ISR)的混合框架。Simon的项目代表了Jamstack的一种极简实现——甚至不需要静态站点生成器,GitHub Actions直接充当了构建管道,GitHub仓库充当了静态资源托管和数据API的双重角色,前端页面则是纯粹的客户端渲染。这种"无框架Jamstack"的做法虽然不适合大型应用,但对于数据展示类的小型项目来说,消除了所有不必要的抽象层,维护成本趋近于零。
这种架构的可靠性来源于其简单性——没有数据库连接池耗尽的风险,没有服务器进程崩溃的可能,没有SSL证书过期的运维负担。GitHub本身的SLA(服务等级协议)为99.9%,对于个人项目而言,这已经是企业级的可用性保障。
对于个人项目和小型工具来说,这种架构模式值得参考——不需要数据库、不需要云服务器,仅靠GitHub的免费功能就能搭建完整的数据应用。
手机上完成完整开发流程
在手机上完成整个项目的开发,这一点值得关注。Claude Code for web使得开发者不再受限于桌面环境,在任何有浏览器的设备上都能进行有效的编程工作。对于露营这样的场景,这种灵活性尤为珍贵。
这也预示着AI辅助编程工具正在重新定义"工作环境"——只要能描述清楚需求,代码生成不再依赖特定的IDE或硬件配置。从更宏观的视角看,这反映了软件开发工具链的一个长期趋势:从本地化走向云端化。早期的开发必须依赖本地安装的编译器和编辑器,后来出现了GitHub Codespaces、Gitpod等云端IDE,而AI编程工具则更进一步——开发者甚至不需要理解项目的文件结构和构建流程,只需要在对话界面中表达意图。这并不意味着开发知识变得不重要,恰恰相反,Simon之所以能用一条提示词完成开发,正是因为他对Web技术栈有深刻理解,知道该要求什么、如何要求。
提示词工程的精确性
Simon的提示词虽然只有一段话,但包含了完整的技术规格:数据源URL、图片尺寸策略、性能优化要求(lazy loading)和交互模式(模态框)和内容要求(物种名称)。这种精确的需求表达是高效使用AI编程工具的关键。
从提示词工程(Prompt Engineering)的方法论角度来看,Simon的提示词体现了几个重要原则。首先是具体性——他没有说"做一个好看的页面",而是明确指定了数据源地址、图片的具体尺寸变体(small.jpg和large.jpg)以及交互行为。其次是技术锚定——通过提及loading=lazy这样的具体HTML属性,他为AI提供了明确的技术实现方向,避免了AI在多种可能的实现方案之间犹豫。最后是完整的上下文边界——提示词隐含了"这是一个纯前端应用"的约束,不需要后端逻辑,数据已经是现成的JSON格式。在代码生成场景中,最有效的提示词往往不是最长的,而是信息密度最高的——每一句话都在缩小AI的搜索空间,引导它走向开发者心中已有的方案。
对开发者的启示
这个项目虽然规模不大,但展示了一种值得借鉴的开发范式:
工具链拆解思维:将复杂需求拆解为独立的、可组合的工具链,每一层都保持简单和可替换。Python CLI负责数据逻辑,Git Scraping负责自动化调度,前端负责展示——各司其职,松耦合高内聚。这种设计哲学在Unix编程传统中有着深厚的根基——"做一件事并做好它"(Do One Thing and Do It Well)。每个组件都可以独立测试、独立替换:如果未来iNaturalist的API发生变化,只需修改Python CLI;如果想换一种前端框架,数据层完全不受影响;如果想把数据源从iNaturalist换成eBird(另一个自然观察平台),只需替换CLI工具,其余管道原封不动。
AI编程的最佳使用场景:当开发者对架构和需求有清晰认知时,AI可以极大加速实现过程,将想法快速转化为可运行的代码。关键不在于让AI替你思考,而在于你能否清晰地表达自己的思考。
Git Scraping的广泛适用性:这种模式适用于任何需要定期获取外部数据的场景——监控API变化、追踪价格波动、记录公开数据集更新等,都可以用同样的方式实现零成本自动化。目前GitHub上已经有数百个公开的Git Scraping仓库在追踪各类数据,从加州野火数据到纽约市餐厅检查评分,从疫苗接种进度到电力市场价格。这种模式的魅力在于它将"数据监控"这个看似需要专业基础设施的任务,降维成了一个只需要一个YAML配置文件和一段脚本的轻量操作。
核心要点
- Simon Willison完全在手机上使用Claude Code for web构建了一个完整的数据展示工具
- 项目采用三层无服务器架构:Python CLI数据处理 + Git Scraping自动化 + 纯静态前端
- 聚合算法将2小时内、5公里内的观察记录归为一组,代表一次完整的野外观察活动
- 利用GitHub原始文件的CORS支持,实现了零成本的数据API托管
- 一条精确的提示词即完成了包含懒加载、模态框等功能的前端页面开发
相关推荐
教程攻略Cursor+Codex双IDE协同:开源项目二开实战方法论
基于实战经验总结的开源项目二次开发完整方法论,详解Cursor+Codex双IDE协同工作流,涵盖二开七环节、MVP验证、AI读源码技巧,帮助开发者三天跑通项目、两周完成业务集成。
教程攻略Cursor多Agent实战:50分钟搭建Next.js全栈博客
使用Cursor IDE多Agent协作模式,50分钟内从零搭建全栈博客。涵盖Next.js、Clerk认证、Supabase数据库集成,详解4个AI Agent分阶段开发流程与关键避坑经验。
教程攻略从零搭建AI软件工厂:Cursor工程师的多Agent协作实战经验
Cursor工程师Eric分享AI软件工厂构建实战:从自动化六层级、护栏设计、并行Agent管理到规模化扩展,详解如何用多Agent协作实现7×24小时高效软件开发。