前提条件#
- Remix 项目
- 一个 Firecrawl API 密钥——免费获取
安装 SDK#
npm install firecrawl将 API 密钥添加到 .env:
FIRECRAWL_API_KEY=fc-YOUR-API-KEY进行网页搜索#
在 action 中使用 Firecrawl 处理表单提交。创建 app/routes/search.tsx:
import { json, type ActionFunctionArgs } from "@remix-run/node";import { Form, useActionData } from "@remix-run/react";import { Firecrawl } from "firecrawl";const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const query = formData.get("query") as string; const results = await firecrawl.search(query, { limit: 5 }); return json({ results: (results.web || []).map((r) => ({ title: r.title, url: r.url })) });}export default function SearchPage() { const data = useActionData<typeof action>(); return ( <div> <Form method="post"> <input name="query" placeholder="进行网页搜索..." /> <button type="submit">搜索</button> </Form> {data?.results?.map((r, i) => ( <div key={i}> <a href={r.url}>{r.title}</a> </div> ))} </div> );}抓取网页#
在 loader 中使用 Firecrawl,在请求时获取数据。创建 app/routes/scrape.tsx:
import { json, type LoaderFunctionArgs } from "@remix-run/node";import { useLoaderData } from "@remix-run/react";import { Firecrawl } from "firecrawl";const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });export async function loader({ request }: LoaderFunctionArgs) { const url = new URL(request.url); const target = url.searchParams.get("url"); if (!target) return json({ markdown: null }); const result = await firecrawl.scrape(target); return json({ markdown: result.markdown });}export default function ScrapePage() { const { markdown } = useLoaderData<typeof loader>(); return ( <div> <h1>Scraped Content</h1> {markdown ? <pre>{markdown}</pre> : <p>Pass ?url= to scrape a page</p>} </div> );}与页面交互#
使用 interact 控制正在运行的浏览器会话——点击按钮、填写表单,并提取动态内容。创建 app/routes/interact.tsx:
import { json, type ActionFunctionArgs } from "@remix-run/node";import { Form, useActionData } from "@remix-run/react";import { Firecrawl } from "firecrawl";const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const url = formData.get("url") as string; const result = await firecrawl.scrape(url, { formats: ['markdown'] }); const scrapeId = result.metadata?.scrapeId; await firecrawl.interact(scrapeId, { prompt: 'Search for iPhone 16 Pro Max' }); const response = await firecrawl.interact(scrapeId, { prompt: 'Click on the first result and tell me the price' }); await firecrawl.stopInteraction(scrapeId); return json({ output: response.output });}export default function InteractPage() { const data = useActionData<typeof action>(); return ( <div> <Form method="post"> <input name="url" placeholder="URL to interact with..." /> <button type="submit">Interact</button> </Form> {data?.output && <pre>{data.output}</pre>} </div> );}
