关于 firecrawl-scraper
firecrawl-scraper 是一项介绍如何使用 Firecrawl 网页数据 API 和 SDK 获取并结构化网站内容的技能。内容涵盖单页抓取、站点爬取、搜索、提取、自主采集、批量任务和变更跟踪。
主要功能
- 01将网页抓取为多种格式
- 02爬取站点并发现 URL
- 03搜索网页并提取结构化数据
- 04使用自主 Agent 收集数据
- 05批量抓取和变更跟踪
- 06通过浏览器操作处理动态页面
适用场景
- 01将文章或商品页转换为结构化内容
- 02为整个网站的文档建立索引
- 03查找并提取商品或公司信息
- 04跟踪价格或文档页面的变化
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill firecrawl-scraper安装后在 Claude Code 中输入/firecrawl-scraper确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.md.claude-plugin/plugin.jsonreferences/common-patterns.mdendpoints.mdtemplates/firecrawl-crawl-example.pyfirecrawl-scrape-python.pyfirecrawl-scrape-typescript.tsfirecrawl-worker-fetch.ts详细介绍
Firecrawl 网页抓取工具
状态:已准备好用于生产环境 最近更新:2026-01-20 官方文档:https://docs.firecrawl.dev API 版本:v2 SDK 版本:firecrawl-py 4.13.0+、@mendable/firecrawl-js 4.11.1+
什么是 Firecrawl?
Firecrawl 是面向 AI 的网页数据 API,可将网站转换为适合大模型使用的 Markdown 或结构化数据。它支持:
- JavaScript 渲染 - 执行客户端 JavaScript,以捕获动态内容
- 反机器人绕过 - 绕过 CAPTCHA 和机器人检测系统
- 格式转换 - 输出 Markdown、HTML、JSON、截图和摘要
- 文档解析 - 处理 PDF、DOCX 文件和图像
- 自主 Agent - 无需 URL 即可通过 AI 收集网页数据
- 变更跟踪 - 监控内容随时间发生的变化
- 品牌信息提取 - 提取配色方案、排版、徽标
API 端点概览
| 端点 | 用途 | 使用场景 |
|---|---|---|
/scrape | 单个页面 | 提取文章、产品页面 |
/crawl | 整个网站 | 为文档编制索引、归档网站 |
/map | URL 发现 | 查找所有页面、规划策略 |
/search | 网页搜索 + 抓取 | 使用实时数据进行研究 |
/extract | 结构化数据 | 产品价格、联系方式 |
/agent | 自主收集 | 无需 URL,由 AI 自主浏览 |
/batch-scrape | 多个 URL | 批量处理 |
1. Scrape 端点(/v2/scrape)
抓取单个网页并返回干净、结构化的内容。
基本用法
from firecrawl import Firecrawl
import os
app = Firecrawl(api_key=os.environ.get("FIRECRAWL_API_KEY"))
# Basic scrape
doc = app.scrape(
url="https://example.com/article",
formats=["markdown", "html"],
only_main_content=True
)
print(doc.markdown)
print(doc.metadata)
import FirecrawlApp from '@mendable/firecrawl-js';
const app = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY });
const result = await app.scrapeUrl('https://example.com/article', {
formats: ['markdown', 'html'],
onlyMainContent: true
});
console.log(result.markdown);
输出格式
| 格式 | 说明 |
|---|---|
markdown | 针对大模型优化的内容 |
html | 完整 HTML |
rawHtml | 未处理的 HTML |
screenshot | 页面截图(支持视口选项) |
links | 页面上的所有 URL |
json | 结构化数据提取 |
summary | AI 生成的摘要 |
branding | 设计系统数据 |
changeTracking | 内容变更检测 |
高级选项
doc = app.scrape(
url="https://example.com",
formats=["markdown", "screenshot"],
only_main_content=True,
remove_base64_images=True,
wait_for=5000, # Wait 5s for JS
timeout=30000,
# Location & language
location={"country": "AU", "languages": ["en-AU"]},
# Cache control
max_age=0, # Fresh content (no cache)
store_in_cache=True,
# Stealth mode for complex sites
stealth=True,
# Custom headers
headers={"User-Agent": "Custom Bot 1.0"}
)
浏览器操作
抓取前执行交互操作:
doc = app.scrape(
url="https://example.com",
actions=[
{"type": "click", "selector": "button.load-more"},
{"type": "wait", "milliseconds": 2000},
{"type": "scroll", "direction": "down"},
{"type": "write", "selector": "input#search", "text": "query"},
{"type": "press", "key": "Enter"},
{"type": "screenshot"} # Capture state mid-action
]
)
JSON 模式(结构化提取)
# With schema
doc = app.scrape(
url="https://example.com/product",
formats=["json"],
json_options={
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"price": {"type": "number"},
"in_stock": {"type": "boolean"}
}
}
}
)
# Without schema (prompt-only)
doc = app.scrape(
url="https://example.com/product",
formats=["json"],
json_options={
"prompt": "Extract the product name, price, and availability"
}
)
品牌信息提取
提取设计系统和品牌标识:
doc = app.scrape(
url="https://example.com",
formats=["branding"]
)
# Returns:
# - Color schemes and palettes
# - Typography (fonts, sizes, weights)
# - Spacing and layout metrics
# - UI component styles
# - Logo and imagery URLs
# - Brand personality traits
2. Crawl 端点(/v2/crawl)
从起始 URL 抓取所有可访问的页面。
result = app.crawl(
url="https://docs.example.com",
limit=100,
max_depth=3,
allowed_domains=["docs.example.com"],
exclude_paths=["/api/*", "/admin/*"],
scrape_options={
"formats": ["markdown"],
"only_main_content": True
}
)
for page in result.data:
print(f"Scraped: {page.metadata.source_url}")
print(f"Content: {page.markdown[:200]}...")
使用 Webhook 的异步抓取
# Start crawl (returns immediately)
job = app.start_crawl(
url="https://docs.example.com",
limit=1000,
webhook="https://your-domain.com/webhook"
)
print(f"Job ID: {job.id}")
# Or poll for status
status = app.check_crawl_status(job.id)
3. Map 端点(/v2/map)
快速发现网站上的所有 URL,无需抓取内容。
urls = app.map(url="https://example.com")
print(f"Found {len(urls)} pages")
for url in urls[:10]:
print(url)
适用于:发现站点地图、规划抓取、网站审计。
4. Search 端点(/search)- 新增
执行网页搜索,并可选择在同一操作中抓取搜索结果。
# Basic search
results = app.search(
query="best practices for React server components",
limit=10
)
for result in results:
print(f"{result.title}: {result.url}")
# Search + scrape results
results = app.search(
query="React server components tutorial",
limit=5,
scrape_options={
"formats": ["markdown"],
"only_main_content": True
}
)
for result in results:
print(f"{result.title}")
print(result.markdown[:500])
搜索选项
results = app.search(
query="machine learning papers",
limit=20,
# Filter by source type
sources=["web", "news", "images"],
# Filter by category
categories=["github", "research", "pdf"],
# Location
location={"country": "US"},
# Time filter
tbs="qdr:m", # Past month (qdr:h=hour, qdr:d=day, qdr:w=week, qdr:y=year)
timeout=30000
)
费用:每 10 条结果消耗 2 个积分;启用抓取时还需支付抓取费用。
5. Extract 端点(/v2/extract)
利用 AI 从单个页面、多个页面或整个域名中提取结构化数据。
单个页面
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
description: str
in_stock: bool
result = app.extract(
urls=["https://example.com/product"],
schema=Product,
system_prompt="Extract product information"
)
print(result.data)
多页面 / 域名提取
# Extract from entire domain using wildcard
result = app.extract(
urls=["example.com/*"], # All pages on domain
schema=Product,
system_prompt="Extract all products"
)
# Enable web search for additional context
result = app.extract(
urls=["example.com/products"],
schema=Product,
enable_web_search=True # Follow external links
)
仅使用提示词提取(无架构)
result = app.extract(
urls=["https://example.com/about"],
prompt="Extract the company name, founding year, and key executives"
)
# LLM determines output structure
6. Agent 端点(/agent)- 新增
无需指定 URL 即可自主收集网页数据。Agent 使用自然语言提示词进行搜索、浏览和数据收集。
# Basic agent usage
result = app.agent(
prompt="Find the pricing plans for the top 3 headless CMS platforms and compare their features"
)
print(result.data)
# With schema for structured output
from pydantic import BaseModel
from typing import List
class CMSPricing(BaseModel):
name: str
free_tier: bool
starter_price: float
features: List[str]
result = app.agent(
prompt="Find pricing for Contentful, Sanity, and Strapi",
schema=CMSPricing
)
# Optional: focus on specific URLs
result = app.agent(
prompt="Extract the enterprise pricing details",
urls=["https://contentful.com/pricing", "https://sanity.io/pricing"]
)
Agent 模型
| 模型 | 最适合的用途 | 费用 |
|---|---|---|
spark-1-mini(默认) | 简单提取、大批量处理 | 标准 |
spark-1-pro | 复杂分析、含糊数据 | 高 60% |
result = app.agent(
prompt="Analyze competitive positioning...",
model="spark-1-pro" # For complex tasks
)
异步 Agent
# Start agent (returns immediately)
job = app.start_agent(
prompt="Research market trends..."
)
# Poll for results
status = app.check_agent_status(job.id)
if status.status == "completed":
print(status.data)
注意:Agent 处于研究预览阶段。每天可免费请求 5 次,之后按积分计费。
7. 批量抓取 - 新增
通过单次操作高效处理多个 URL。
同步(等待完成)
results = app.batch_scrape(
urls=[
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3"
],
formats=["markdown"],
only_main_content=True
)
for page in results.data:
print(f"{page.metadata.source_url}: {len(page.markdown)} chars")
异步(使用 Webhook)
job = app.start_batch_scrape(
urls=url_list,
formats=["markdown"],
webhook="https://your-domain.com/webhook"
)
# Webhook receives events: started, page, completed, failed
const job = await app.startBatchScrape(urls, {
formats: ['markdown'],
webhook: 'https://your-domain.com/webhook'
});
// Poll for status
const status = await app.checkBatchScrapeStatus(job.id);
8. 变更跟踪 - 新增
通过比较抓取结果,监控内容随时间发生的变化。
# Enable change tracking
doc = app.scrape(
url="https://example.com/pricing",
formats=["markdown", "changeTracking"]
)
# Response includes:
print(doc.change_tracking.status) # new, same, changed, removed
print(doc.change_tracking.previous_scrape_at)
print(doc.change_tracking.visibility) # visible, hidden
比较模式
# Git-diff mode (default)
doc = app.scrape(
url="https://example.com/docs",
formats=["markdown", "changeTracking"],
change_tracking_options={
"mode": "diff"
}
)
print(doc.change_tracking.diff) # Line-by-line changes
# JSON mode (structured comparison)
doc = app.scrape(
url="https://example.com/pricing",
formats=["markdown", "changeTracking"],
change_tracking_options={
"mode": "json",
"schema": {"type": "object", "properties": {"price": {"type": "number"}}}
}
)
# Costs 5 credits per page
变更状态:
new- 之前未见过的页面same- 自上次抓取以来没有变化changed- 内容已修改removed- 页面已无法访问
身份验证
# Get API key from https://www.firecrawl.dev/app
# Store in environment
FIRECRAWL_API_KEY=fc-your-api-key-here
切勿将 API 密钥硬编码!
Cloudflare Workers 集成
Firecrawl SDK 无法在 Cloudflare Workers 中运行(需要 Node.js)。请直接使用 REST API:
interface Env {
FIRECRAWL_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { url } = await request.json<{ url: string }>();
const response = await fetch('https://api.firecrawl.dev/v2/scrape', {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.FIRECRAWL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
formats: ['markdown'],
onlyMainContent: true
})
});
const result = await response.json();
return Response.json(result);
}
};
速率限制与定价
警告:隐身模式定价变更(2025 年 5 月)
主动使用隐身模式时,每个请求现在需要 5 个积分。默认行为使用“auto”模式,只有基础模式失败时才会收取隐身积分。
推荐模式:
# Use auto mode (default) - only charges 5 credits if stealth is needed
doc = app.scrape(url, formats=["markdown"])
# Or conditionally enable stealth for specific errors
if error_status_code in [401, 403, 500]:
doc = app.scrape(url, formats=["markdown"], proxy="stealth")
统一计费(2025 年 11 月)
积分和令牌合并为一个系统。Extract 端点使用积分(15 个令牌 = 1 个积分)。
定价等级
| 等级 | 每月积分 | 备注 |
|---|---|---|
| 免费 | 500 | 适合测试 |
| Hobby | 3,000 | $19/月 |
| Standard | 100,000 | $99/月 |
| Growth | 500,000 | $399/月 |
积分费用:
- 抓取:1 个积分(基础模式),5 个积分(隐身模式)
- Crawl:每页 1 个积分
- 搜索:每 10 条结果 2 个积分
- 提取:每页 5 个积分(在 v2.6.0 中从令牌改为积分)
- Agent:动态计费(根据复杂度)
- 变更跟踪 JSON 模式:+5 个积分
常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 内容为空 | JS 未加载 | 添加 wait_for: 5000 或使用 actions |
| 超出速率限制 | 超出配额 | 查看控制面板,升级套餐 |
| 超时错误 | 页面加载缓慢 | 增加 timeout,使用 stealth: true |
| 检测到机器人 | 反爬虫机制 | 使用 stealth: true,添加 location |
| API 密钥无效 | 格式错误 | 必须以 fc- 开头 |
预防已知问题
此技能可预防 10 个已记录的问题:
问题 #1:隐身模式定价变更(2025 年 5 月)
错误:使用隐身模式时积分费用异常 来源:隐身模式文档 | 变更日志 发生原因:从 2025 年 5 月 8 日起,隐身模式代理请求每次需要 5 个积分(此前已包含在标准定价中)。这是一次重大的计费变更。 预防措施:使用 auto 模式(默认),只有基础模式失败时才会收取隐身积分
# RECOMMENDED: Use auto mode (default)
doc = app.scrape(url, formats=['markdown'])
# Auto retries with stealth (5 credits) only if basic fails
# Or conditionally enable based on error status
try:
doc = app.scrape(url, formats=['markdown'], proxy='basic')
except Exception as e:
if e.status_code in [401, 403, 500]:
doc = app.scrape(url, formats=['markdown'], proxy='stealth')
隐身模式选项:
auto(默认):仅在基础模式失败后隐身模式成功时收取 5 个积分basic:标准代理,费用为 1 个积分stealth:主动使用时,每个请求需要 5 个积分
问题 #2:v2.0.0 重大变更——方法重命名
错误:AttributeError: 'FirecrawlApp' object has no attribute 'scrape_url'
来源:v2.0.0 发布说明 | 迁移指南
发生原因:v2.0.0(2025 年 8 月)重命名了所有语言 SDK 中的方法
预防措施:使用新的方法名称
JavaScript/TypeScript:
scrapeUrl()→scrape()crawlUrl()→crawl()或startCrawl()asyncCrawlUrl()→startCrawl()checkCrawlStatus()→getCrawlStatus()
Python:
scrape_url()→scrape()crawl_url()→crawl()或start_crawl()
# OLD (v1)
doc = app.scrape_url("https://example.com")
# NEW (v2)
doc = app.scrape("https://example.com")
问题 #3:v2.0.0 重大变更——格式变更
错误:'extract' is not a valid format
来源:v2.0.0 发布说明
发生原因:旧的 "extract" 格式在 v2.0.0 中更名为 "json"
预防措施:使用新的对象格式进行 JSON 提取
# OLD (v1)
doc = app.scrape_url(
url="https://example.com",
params={
"formats": ["extract"],
"extract": {"prompt": "Extract title"}
}
)
# NEW (v2)
doc = app.scrape(
url="https://example.com",
formats=[{"type": "json", "prompt": "Extract title"}]
)
# With schema
doc = app.scrape(
url="https://example.com",
formats=[{
"type": "json",
"prompt": "Extract product info",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"price": {"type": "number"}
}
}
}]
)
截图格式也已变更:
# NEW: Screenshot as object
formats=[{
"type": "screenshot",
"fullPage": True,
"quality": 80,
"viewport": {"width": 1920, "height": 1080}
}]
问题 #4:v2.0.0 破坏性更改——爬取选项
错误:'allowBackwardCrawling' is not a valid parameter
来源:v2.0.0 Release
发生原因:v2.0.0 中有多个爬取参数被重命名或移除
预防措施:使用新的参数名称
参数更改:
allowBackwardCrawling→ 改用crawlEntireDomainmaxDepth→ 改用maxDiscoveryDepthignoreSitemap(bool)→sitemap("only"、"skip"、"include")
# OLD (v1)
app.crawl_url(
url="https://docs.example.com",
params={
"allowBackwardCrawling": True,
"maxDepth": 3,
"ignoreSitemap": False
}
)
# NEW (v2)
app.crawl(
url="https://docs.example.com",
crawl_entire_domain=True,
max_discovery_depth=3,
sitemap="include" # "only", "skip", or "include"
)
问题 #5:v2.0.0 默认行为更改
错误:意外返回过期的缓存内容 来源:v2.0.0 Release 发生原因:v2.0.0 更改了多个默认值 预防措施:留意新的默认值
默认值更改:
maxAge现在默认为 2 天(默认启用缓存)blockAds、skipTlsVerification、removeBase64Images默认启用
# Force fresh data if needed
doc = app.scrape(url, formats=['markdown'], max_age=0)
# Disable cache entirely
doc = app.scrape(url, formats=['markdown'], store_in_cache=False)
问题 #6:作业状态竞争条件
错误:创建爬取作业后立即检查状态时出现 "Job not found"
来源:GitHub Issue #2662
发生原因:作业创建与状态端点可用之间存在数据库复制延迟
预防措施:首次检查状态前等待 1-3 秒,或实现重试逻辑
import time
# Start crawl
job = app.start_crawl(url="https://docs.example.com")
print(f"Job ID: {job.id}")
# REQUIRED: Wait before first status check
time.sleep(2) # 1-3 seconds recommended
# Now status check succeeds
status = app.get_crawl_status(job.id)
# Or implement retry logic
def get_status_with_retry(job_id, max_retries=3, delay=1):
for attempt in range(max_retries):
try:
return app.get_crawl_status(job_id)
except Exception as e:
if "Job not found" in str(e) and attempt < max_retries - 1:
time.sleep(delay)
continue
raise
status = get_status_with_retry(job.id)
问题 #7:DNS 错误返回 HTTP 200
错误:DNS 解析失败时返回 success: false 和 HTTP 200 状态,而不是 4xx
来源:GitHub Issue #2402 | 已在 v2.7.0 中修复
发生原因:v2.7.0 中进行了更改,以统一错误处理
预防措施:检查 success 字段和 code 字段,不要只依赖 HTTP 状态
const result = await app.scrape('https://nonexistent-domain-xyz.com');
// DON'T rely on HTTP status code
// Response: HTTP 200 with { success: false, code: "SCRAPE_DNS_RESOLUTION_ERROR" }
// DO check success field
if (!result.success) {
if (result.code === 'SCRAPE_DNS_RESOLUTION_ERROR') {
console.error('DNS resolution failed');
}
throw new Error(result.error);
}
注意:即使 DNS 解析失败,仍会扣除 1 个积分。
问题 #8:机器人检测仍会扣除积分
错误:Cloudflare 错误页面被作为“成功”的抓取结果返回,并扣除积分 来源:GitHub Issue #2413 发生原因:即使机器人检测阻止了访问,Fire-1 引擎仍会扣除积分 预防措施:处理内容前,确认其不是错误页面;对于受保护的网站,使用隐身模式
# First attempt without stealth
doc = app.scrape(url="https://protected-site.com", formats=["markdown"])
# Validate content isn't an error page
if "cloudflare" in doc.markdown.lower() or "access denied" in doc.markdown.lower():
# Retry with stealth (costs 5 credits if successful)
doc = app.scrape(url, formats=["markdown"], stealth=True)
成本影响:即使失败,基础抓取也会扣除 1 个积分,隐身模式重试会额外扣除 5 个积分。
问题 #9:自托管反机器人指纹识别能力不足
错误:在启用了反机器人措施的网站上出现 "All scraping engines failed!"(SCRAPE_ALL_ENGINES_FAILED)
来源:GitHub Issue #2257
发生原因:自托管 Firecrawl 缺少云服务所具备的高级反指纹识别技术
预防措施:对于启用了强反机器人措施的网站,使用 Firecrawl 云服务或配置代理
# Self-hosted fails on Cloudflare-protected sites
curl -X POST 'http://localhost:3002/v2/scrape' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{
"url": "https://www.example.com/",
"pageOptions": { "engine": "playwright" }
}'
# Error: "All scraping engines failed!"
# Workaround: Use cloud service instead
# Cloud service has better anti-fingerprinting
注意:此问题影响使用默认 docker-compose 配置的自托管 v2.3.0+。系统会显示警告:“⚠️ WARNING: No proxy server provided. Your IP address may be blocked.”
问题 #10:缓存性能最佳实践(来自社区)
性能欠佳:未利用缓存可能导致请求速度慢 500%
来源:Fast Scraping Docs | Blog Post
重要原因:v2+ 中 maxAge 默认为 2 天,但许多使用场景需要采用不同的策略
预防措施:根据内容类型使用适当的缓存策略
# Fresh data (real-time pricing, stock prices)
doc = app.scrape(url, formats=["markdown"], max_age=0)
# 10-minute cache (news, blogs)
doc = app.scrape(url, formats=["markdown"], max_age=600000) # milliseconds
# Use default cache (2 days) for static content
doc = app.scrape(url, formats=["markdown"]) # maxAge defaults to 172800000
# Don't store in cache (one-time scrape)
doc = app.scrape(url, formats=["markdown"], store_in_cache=False)
# Require minimum age before re-scraping (v2.7.0+)
doc = app.scrape(url, formats=["markdown"], min_age=3600000) # 1 hour minimum
性能影响:
- 缓存响应:毫秒级
- 全新抓取:秒级
- 速度差异:最高可达 500%
软件包版本
| 软件包 | 版本 | 上次检查时间 |
|---|---|---|
| firecrawl-py | 4.13.0+ | 2026-01-20 |
| @mendable/firecrawl-js | 4.11.1+ | 2026-01-20 |
| API 版本 | v2 | 当前 |
官方文档
- 文档:https://docs.firecrawl.dev
- Python SDK:https://docs.firecrawl.dev/sdks/python
- Node.js SDK:https://docs.firecrawl.dev/sdks/node
- API 参考:https://docs.firecrawl.dev/api-reference
- GitHub:https://github.com/mendableai/firecrawl
- 控制面板:https://www.firecrawl.dev/app
节省令牌:与手动集成相比约节省 65% 预防错误:记录了 10 个问题(v2 迁移、隐身模式定价、任务状态竞态、DNS 错误、机器人检测计费、可自托管版本的限制、缓存优化) 可用于生产环境:是 上次验证时间:2026-01-21 | 技能版本:2.0.0 | 变更:新增“已知问题预防”章节,收录 TIER 1-2 研究结果中记录的 10 个错误;新增 v2 迁移指南;记录隐身模式定价变更和统一计费模式