【数据采集】[特殊字符] Firecrawl 示例页面 —— 技术设计、原理与部署全解(二)
2026/9/4 2:10:53 网站建设 项目流程

将任意网站转化为 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-sdkpage_reader函数)
数据库Prisma ORM + SQLite
构建工具Bun
反向代理Caddy
部署模式Standalone 产物 + 进程编排

🧩 系统架构

应用采用经典的“单页前端 + API 后端 + 进程编排”三层结构。

  • 前端通过 HTTP 调用后端 API;
  • 后端借助z-aiSDK 获取目标网页并转换为 Markdown;
  • 部署层由 Caddy 统一入口,将请求分发到 Next.js 服务和可选的 mini-services。

HTTP / fetch

带 ?XTransformPort=N 的请求

其余请求

/api/scrape

调用

无头浏览器抓取

htmlToMarkdown

Prisma

浏览器(用户)

Caddy 反向代理 :81

Mini-Services 子服务 :N

Next.js Standalone :3000

Route Handler

z-ai-web-dev-sdk
page_reader

目标网页 HTML

清洗后 Markdown

SQLite

请求流转(以 Live Demo 为例)

当用户在LiveDemo中输入 URL 并点击Scrape时,数据流经以下路径:

  1. 前端发起POST /api/scrape,请求体{ url: string }
  2. Caddy将请求转发到 Next.js 的3000端口。
  3. Route Handler校验 URL 合法性,调用ZAI.create()初始化 SDK。
  4. SDK 的page_reader函数无头抓取目标页面,返回标题、HTML、发布时间等元数据。
  5. htmlToMarkdown()将原始 HTML 清洗并转换为Markdown
  6. 后端组装响应(markdownstatsfetchedAt),返回 JSON。
  7. 前端在Markdown / Stats / Raw三个标签页中展示结果。

⚙️ 核心原理解析

🔍 网页抓取链路

抓取链路的灵魂是z-ai-web-dev-sdk提供的page_reader函数。它封装了无头浏览器渲染 + 内容提取的完整流程:

constzai=awaitZAI.create();constresult=awaitzai.functions.invoke("page_reader",{url});

page_reader的工作分为三个阶段:

  1. 页面渲染—— 启动无头 Chrome,导航到目标 URL,等待页面加载完成。这保证了 JavaScript 渲染的 SPA、动态加载内容都能被正确捕获。
  2. 内容提取—— 从渲染后的 DOM 中提取标题、最终 URL(经重定向)、完整 HTML 和发布时间(检查多种 meta 标签)。
  3. 用量统计—— 返回usage.tokens,记录本次抓取消耗的 token 数,用于计费和监控。

拿到原始 HTML 后,后端调用自定义的htmlToMarkdown()进行清洗和格式转换 —— 这是整个应用的“价值放大器”


🧹 HTML 转 Markdown 算法

htmlToMarkdown()位于src/lib/html-to-markdown.ts,用纯正则 + 递归下降实现了一个轻量级转换器,不依赖第三方库,约 150 行代码。

处理管线:

原始 HTML

剥离噪声标签
script/style/nav/footer/注释

提取 main/article
聚焦主内容区

块级元素转换
标题/代码块/引用/列表/表格

行内元素转换
链接/图片/加粗/斜体/代码

清理残余标签 & 压缩空行

干净 Markdown

关键设计亮点:

  • 噪声剥离优先:移除<script><style><nav><footer><header><svg>等非内容标签,避免污染统计和 token 消耗。
  • 递归下降:对<div><section><article>等容器递归处理,能应对任意深度的嵌套。
  • 行内与块级分离inlineToMarkdown()专注处理链接、图片、加粗、斜体、代码等,在块级转换中被反复调用,职责单一。
  • HTML 实体解码:支持&nbsp;&amp;&#39;等命名、数字和十六进制实体,确保输出无转义残留。
  • 表格支持:能将简单 HTML 表格转换为 Markdown 管道表格格式。

取舍说明:正则方案比 DOM 解析更快且零依赖,但无法处理畸形 HTML。对于结构良好的网页(博客、文档站、新闻页),输出质量足以支撑 LLM 消费。


🚀 部署架构原理

📦 Standalone 构建模式

next.config.ts中声明output: "standalone",这是 Next.js 的生产部署优化模式。构建时会将所有必需的node_modules依赖追踪并打包进.next/standalone/目录,产出一个自包含的server.js,可直接用bun server.jsnode server.js运行,无需在部署环境安装依赖。

构建脚本build.sh包含“自愈”机制:如果构建后server.js不存在(例如误删配置),会自动注入配置并重新构建。

完整构建收集流程:

bun install

bun run build
(next build)

生成 .next/standalone/server.js

生成 .next/static/

复制 public/

复制到 next-service-dist/

database-runtime-build.sh
初始化数据库

python-runtime-build.sh
固化 Python 依赖

mini-services-build.sh
编译子服务

tar -czf 打包

🌐 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.txtpyproject.toml
  • uv将生产依赖安装到构建产物的python-runtime/site-packages/
  • 修复 console scripts 的 shebang,使运行时能正确解析。
  • 复制源码到部署产物,保持相对路径。

start.sh运行时检测到该目录后,会将其加入PYTHONPATHPATH,使 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>= 20Next.js 运行时基础
Caddy>= 2生产环境反向代理
uv(可选)>= 0.4Python 依赖管理(如有 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

启动顺序:

  1. 检测并配置 Python 运行时(如有)
  2. 启动 Next.js Standalone 服务(bun server.js,后台运行)
  3. 校验数据库文件存在性
  4. 启动 mini-services(如有,后台运行)
  5. 前台启动 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": truemarkdown字段和统计数据。


📊 数据模型(Prisma + SQLite)

项目预设了UserPost模型(一对多关系),虽然当前抓取功能不直接使用,但为后续扩展(如保存抓取历史、用户管理)奠定基础。

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”服务,还是学习项目的架构与部署,这个项目都值得深入研究和借鉴。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询