mcpField
F
firecrawl-scraper

firecrawl-scraper

github.com/jezweb数据与数据库1.1k518 次安装3 周前更新
代码仓库

关于 firecrawl-scraper

firecrawl-scraper 是一项介绍如何使用 Firecrawl 网页数据 API 和 SDK 获取并结构化网站内容的技能。内容涵盖单页抓取、站点爬取、搜索、提取、自主采集、批量任务和变更跟踪。

主要功能

  1. 01将网页抓取为多种格式
  2. 02爬取站点并发现 URL
  3. 03搜索网页并提取结构化数据
  4. 04使用自主 Agent 收集数据
  5. 05批量抓取和变更跟踪
  6. 06通过浏览器操作处理动态页面

适用场景

  1. 01将文章或商品页转换为结构化内容
  2. 02为整个网站的文档建立索引
  3. 03查找并提取商品或公司信息
  4. 04跟踪价格或文档页面的变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill firecrawl-scraper

安装后在 Claude Code 中输入/firecrawl-scraper确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
references/
common-patterns.md
endpoints.md
templates/
firecrawl-crawl-example.py
firecrawl-scrape-python.py
firecrawl-scrape-typescript.ts
firecrawl-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整个网站为文档编制索引、归档网站
/mapURL 发现查找所有页面、规划策略
/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结构化数据提取
summaryAI 生成的摘要
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适合测试
Hobby3,000$19/月
Standard100,000$99/月
Growth500,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 → 改用 crawlEntireDomain
  • maxDepth → 改用 maxDiscoveryDepth
  • ignoreSitemap(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-py4.13.0+2026-01-20
@mendable/firecrawl-js4.11.1+2026-01-20
API 版本v2当前

官方文档


节省令牌:与手动集成相比约节省 65% 预防错误:记录了 10 个问题(v2 迁移、隐身模式定价、任务状态竞态、DNS 错误、机器人检测计费、可自托管版本的限制、缓存优化) 可用于生产环境:是 上次验证时间:2026-01-21 | 技能版本:2.0.0 | 变更:新增“已知问题预防”章节,收录 TIER 1-2 研究结果中记录的 10 个错误;新增 v2 迁移指南;记录隐身模式定价变更和统一计费模式

评论

支持 Markdown