将任意网站转化为 LLM 可用数据的展示型全栈应用。
📌 项目概述
Firecrawl 示例页面是一个基于Next.js 16的全栈 Web 应用,它复现了 Firecrawl 的核心价值主张:将任意网页抓取为干净的 Markdown,直接供大语言模型(LLM)消费。
应用本身是一个“展示型 + 功能型”页面 —— 既有营销落地页的视觉冲击(火焰主题、动画、渐变),又有可实际操作的在线抓取演示(Live Demo)。用户输入任意 URL,即可获得清洗后的 Markdown、统计数据和原始 HTML。
| 维度 | 选型 |
|---|---|
| 前端框架 | Next.js 16(App Router)+ React 19 |
| 样式方案 | Tailwind CSS 4 + shadcn/ui(new-york 风格) |
| 动画库 | Framer Motion |
| 后端运行时 | Node.js(Next.js Route Handler) |
| 数据抓取 | z-ai-web-dev-sdk(page_reader函数) |
| 数据库 | Prisma ORM + SQLite |
| 构建工具 | Bun |
| 反向代理 | Caddy |
| 部署模式 | Standalone 产物 + 进程编排 |
🧩 系统架构
应用采用经典的“单页前端 + API 后端 + 进程编排”三层结构。
- 前端通过 HTTP 调用后端 API;
- 后端借助
z-aiSDK 获取目标网页并转换为 Markdown; - 部署层由 Caddy 统一入口,将请求分发到 Next.js 服务和可选的 mini-services。
请求流转(以 Live Demo 为例)
当用户在LiveDemo中输入 URL 并点击Scrape时,数据流经以下路径:
- 前端发起
POST /api/scrape,请求体{ url: string }。 - Caddy将请求转发到 Next.js 的
3000端口。 - Route Handler校验 URL 合法性,调用
ZAI.create()初始化 SDK。 - SDK 的
page_reader函数无头抓取目标页面,返回标题、HTML、发布时间等元数据。 htmlToMarkdown()将原始 HTML 清洗并转换为Markdown。- 后端组装响应(
markdown、stats、fetchedAt),返回 JSON。 - 前端在Markdown / Stats / Raw三个标签页中展示结果。
⚙️ 核心原理解析
🔍 网页抓取链路
抓取链路的灵魂是z-ai-web-dev-sdk提供的page_reader函数。它封装了无头浏览器渲染 + 内容提取的完整流程:
constzai=awaitZAI.create();constresult=awaitzai.functions.invoke("page_reader",{url});page_reader的工作分为三个阶段:
- 页面渲染—— 启动无头 Chrome,导航到目标 URL,等待页面加载完成。这保证了 JavaScript 渲染的 SPA、动态加载内容都能被正确捕获。
- 内容提取—— 从渲染后的 DOM 中提取标题、最终 URL(经重定向)、完整 HTML 和发布时间(检查多种 meta 标签)。
- 用量统计—— 返回
usage.tokens,记录本次抓取消耗的 token 数,用于计费和监控。
拿到原始 HTML 后,后端调用自定义的htmlToMarkdown()进行清洗和格式转换 —— 这是整个应用的“价值放大器”。
🧹 HTML 转 Markdown 算法
htmlToMarkdown()位于src/lib/html-to-markdown.ts,用纯正则 + 递归下降实现了一个轻量级转换器,不依赖第三方库,约 150 行代码。
处理管线:
关键设计亮点:
- 噪声剥离优先:移除
<script>、<style>、<nav>、<footer>、<header>、<svg>等非内容标签,避免污染统计和 token 消耗。 - 递归下降:对
<div>、<section>、<article>等容器递归处理,能应对任意深度的嵌套。 - 行内与块级分离:
inlineToMarkdown()专注处理链接、图片、加粗、斜体、代码等,在块级转换中被反复调用,职责单一。 - HTML 实体解码:支持
、&、'等命名、数字和十六进制实体,确保输出无转义残留。 - 表格支持:能将简单 HTML 表格转换为 Markdown 管道表格格式。
取舍说明:正则方案比 DOM 解析更快且零依赖,但无法处理畸形 HTML。对于结构良好的网页(博客、文档站、新闻页),输出质量足以支撑 LLM 消费。
🚀 部署架构原理
📦 Standalone 构建模式
next.config.ts中声明output: "standalone",这是 Next.js 的生产部署优化模式。构建时会将所有必需的node_modules依赖追踪并打包进.next/standalone/目录,产出一个自包含的server.js,可直接用bun server.js或node server.js运行,无需在部署环境安装依赖。
构建脚本build.sh包含“自愈”机制:如果构建后server.js不存在(例如误删配置),会自动注入配置并重新构建。
完整构建收集流程:
🌐 Caddy 反向代理
Caddyfile配置了基于查询参数的智能路由:
:81 { @transform_port_query { query XTransformPort=* } handle @transform_port_query { reverse_proxy localhost:{query.XTransformPort} } handle { reverse_proxy localhost:3000 } }这个设计使得所有子服务(mini-services)都可通过同一个81端口访问 —— 前端只需在 URL 中附加?XTransformPort=3003,Caddy 自动将请求路由到对应端口,解决了端口暴露和跨域问题。
🗄️ 数据库运行时处理
database-runtime-build.sh在构建阶段处理数据库:若 Preview 环境已有db/custom.db,则复制;否则初始化空数据库,并对构建产物中的数据库执行prisma db push同步 schema。start.sh运行时检查数据库文件是否存在(默认路径/app/db/custom.db),若缺失则直接终止启动,避免连接到空数据库。也可通过DATABASE_URL环境变量指定外部数据库。
🐍 Python 运行时支持
python-runtime-build.sh处理可选的 Python 依赖:
- 检测项目中是否存在
.py源文件、requirements.txt或pyproject.toml。 - 用
uv将生产依赖安装到构建产物的python-runtime/site-packages/。 - 修复 console scripts 的 shebang,使运行时能正确解析。
- 复制源码到部署产物,保持相对路径。
start.sh运行时检测到该目录后,会将其加入PYTHONPATH和PATH,使 Next.js 及其子进程能直接使用打包的 Python 环境。
📚 Firecrawl API 使用指南
Firecrawl 提供了一整套将网页转化为 LLM 就绪数据的 API,包括搜索、抓取、交互等核心能力。以下为快速上手示例。
🔑 无密钥快速开始
无需 API Key 即可体验基础功能(有速率限制)。需要更高限制时,可在 Firecrawl App 获取 Key 并添加请求头Authorization: Bearer $FIRECRAWL_API_KEY。
🔎 1. 搜索(Search)
搜索网络并返回结果页面的完整内容。
# cURLcurl-s-XPOST"https://api.firecrawl.dev/v2/search"\-H"Content-Type: application/json"\-d'{"query": "firecrawl", "limit": 3}'# Python SDKfromfirecrawlimportFirecrawl firecrawl=Firecrawl()# 无需 API Keyresults=firecrawl.search("firecrawl",limit=3)print(results)// Node.js SDKimport{Firecrawl}from'firecrawl';constfirecrawl=newFirecrawl();constresults=awaitfirecrawl.search('firecrawl',{limit:3});console.log(results);📄 2. 抓取(Scrape)
抓取任意 URL,获得 Markdown、HTML 或结构化 JSON。
curl-s-XPOST"https://api.firecrawl.dev/v2/scrape"\-H"Content-Type: application/json"\-d'{"url": "https://firecrawl.dev", "formats": ["markdown", "html"]}'返回示例(节选):
{"success":true,"data":{"markdown":"# Home - Firecrawl\n\n...","html":"<!DOCTYPE html>...","metadata":{"title":"Home - Firecrawl","description":"..."}}}🖱️ 3. 交互(Interact)
抓取页面后,可以继续与之交互:点击按钮、填写表单、提取动态内容等。
# 1. 抓取 Amazon 首页result=app.scrape("https://www.amazon.com",formats=["markdown"])scrape_id=result.metadata.scrape_id# 2. 搜索产品并获取价格app.interact(scrape_id,prompt="Search for iPhone 16 Pro Max")response=app.interact(scrape_id,prompt="Click on the first result and tell me the price")print(response.output)# 3. 停止会话app.stop_interaction(scrape_id)🧩 更多能力
| 功能 | 说明 |
|---|---|
| Map | 发现网站上的所有 URL |
| Crawl | 递归抓取整个网站 |
| Parse | 将本地 PDF、DOCX、XLSX、HTML 等转换为 Markdown 或 JSON |
| Browser Sandbox | 托管浏览器会话,适用于交互式工作流 |
| Webhooks | 异步事件通知 |
🛠️ 安装与部署(示例项目)
环境要求
| 依赖 | 版本要求 | 用途 |
|---|---|---|
| Bun | >= 1.0 | 包管理 + 构建 + 运行时 |
| Node.js | >= 20 | Next.js 运行时基础 |
| Caddy | >= 2 | 生产环境反向代理 |
| uv(可选) | >= 0.4 | Python 依赖管理(如有 Python 源码) |
本地开发
# 1. 安装依赖buninstall# 2. 初始化数据库bun run db:push# 3. 启动开发服务器bun run dev访问http://localhost:3000,支持热重载。也可使用一键脚本:
sh.zscripts/dev.sh生产构建
# 构建 Next.js 应用bun run build# 使用完整构建脚本(含子服务、Python 等)BUILD_ID=<唯一标识>sh.zscripts/build.sh构建产物为/tmp/build_fullstack_${BUILD_ID}.tar.gz。
部署启动
将构建产物解压到部署目录后,执行:
shstart.sh启动顺序:
- 检测并配置 Python 运行时(如有)
- 启动 Next.js Standalone 服务(
bun server.js,后台运行) - 校验数据库文件存在性
- 启动 mini-services(如有,后台运行)
- 前台启动 Caddy(主进程,监听
81端口)
验证部署
# 健康检查curlhttp://localhost:81/# API 自描述curlhttp://localhost:81/api/scrape# 抓取测试curl-XPOST http://localhost:81/api/scrape\-H"Content-Type: application/json"\-d'{"url": "https://example.com"}'成功响应应包含"success": true、markdown字段和统计数据。
📊 数据模型(Prisma + SQLite)
项目预设了User和Post模型(一对多关系),虽然当前抓取功能不直接使用,但为后续扩展(如保存抓取历史、用户管理)奠定基础。
model User { id String @id @default(cuid()) email String @unique name String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Post { id String @id @default(cuid()) title String content String? published Boolean @default(false) authorId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }数据库连接采用单例模式,避免 Next.js 热重载时反复创建 PrismaClient 实例。
📂 目录结构速览
firecrawl/ ├── src/ │ ├── app/ │ │ ├── api/ │ │ │ ├── route.ts # 健康检查 │ │ │ └── scrape/route.ts # 核心抓取 API │ │ ├── globals.css # 火焰主题 │ │ ├── layout.tsx # 根布局 + SEO │ │ └── page.tsx # 首页(组装所有区块) │ ├── components/ │ │ ├── firecrawl/ # 8 个业务组件 │ │ └── ui/ # shadcn/ui 40+ 基础组件 │ ├── hooks/ # use-mobile, use-toast │ └── lib/ │ ├── db.ts # Prisma 单例 │ ├── html-to-markdown.ts # 转换器 │ └── utils.ts # 类名工具 ├── prisma/ │ └── schema.prisma ├── examples/websocket/ # WebSocket 聊天示例 ├── mini-services/ # 子服务目录 ├── .zscripts/ # 构建与部署脚本 ├── Caddyfile # 反向代理配置 ├── next.config.ts └── package.json🎨 火焰主题设计系统
项目使用一套鲜明的视觉语言,定义在globals.css中:
| 类名 | 作用 | 实现 |
|---|---|---|
.fire-gradient-text | 橙红渐变文字 | linear-gradient+background-clip: text |
.fire-gradient-bg | 橙红渐变背景 | linear-gradient(135deg, #fb923c, #f97316, #ef4444) |
.fire-glow | 大范围火焰光晕 | 双层box-shadow(橙色 + 红色) |
.fire-grid | 透视网格背景 | 双向渐变 + 径向mask-image渐隐 |
.fire-radial | 径向火焰光 | 双椭圆radial-gradient叠加 |
动画:flame-flicker(2.6s 呼吸循环)和float-up(粒子向上飘升),加载骨架屏使用shimmer橙色高光扫过。
🔚 总结
Firecrawl不仅是一个功能完整的网页抓取工具,更是一个技术演示的范本:
- 前端展示与交互设计精良,采用现代 React 生态;
- 后端抓取链路清晰,自定义转换算法轻量高效;
- 部署架构兼顾生产环境需求,通过 Caddy + Standalone 模式实现一体化交付;
- 丰富的 API 能力(搜索、抓取、交互)使其成为 LLM 应用的数据源利器。
无论是想快速搭建自己的“网页转 Markdown”服务,还是学习项目的架构与部署,这个项目都值得深入研究和借鉴。