上个月,我正在给客户做一个竞品价格看板。需求听起来很简单:追踪十二个不同电商网站的商品价格,然后把数据塞进 pandas DataFrame 里做分析。我本来打算像往常一样,直接用 requests 和 BeautifulSoup 搞定。结果三天过去了,我给五个网站写了专门的解析器,处理了三个不同的 JavaScript 渲染问题,而且每次商家一更新 HTML 布局,我的爬虫就挂掉。我花了 80% 的时间在搞这些修修补补的脏活上,只有 20% 的时间在做真正的数据科学。
就在这时候,有个同事建议我试试 Firecrawl。我一开始挺怀疑的——再来一个爬虫 API,感觉也就是把问题换个地方而已。但我还是试了一把,结果它真的改变了我以后做数据科学项目时获取网页数据的方式。接下来我就带你看看我学到了什么,包括我踩过的一些坑。
Firecrawl 解决的核心痛点
传统的爬虫拿到的都是原始 HTML。但在数据科学里,你需要的是干净的结构化数据——比如可以做分词的 markdown 文本,可以直接加载进 DataFrame 的 JSON 对象,或者是能拿去处理的截图。Firecrawl 把这整套清洗流程压缩成了一次 API 调用。你只要给它一个 URL,它就能返回对大模型友好的(LLM-ready)数据。
初始配置
首先,安装 Python SDK,然后去 firecrawl.dev 获取一个 API key(他们有免费额度,不需要绑信用卡):
pip install firecrawl-py
from firecrawl import Firecrawl
app = Firecrawl(api_key="fc-YOUR_API_KEY")
这就搞定了。不用装浏览器驱动,不用搞 HTML 解析器,也不用管代理配置。
用法 1:快速抓取为 Markdown
我的第一个使用场景是为一个 NLP 项目抓取文章文本。我需要的是干净的内容,而不是一团乱麻的 HTML 代码。
result = app.scrape('https://example.com/blog-post')
# Markdown 内容直接就有了
print(result['markdown'][:500])
# 还会自动抓取元数据
print(result['metadata']['title'])
print(result['metadata']['description'])
我把这结果直接喂进了分词管线。不用写正则去清洗,不用去标签——直接就能跑。输出的 Markdown 很好地保留了文档结构(标题、列表、代码块),这对 RAG 系统里的分块(chunking)策略简直太有用了。
惊喜发现: 即便是 JavaScript 渲染的页面,也能完整抓取回来。我拿一个之前把我的 Selenium 搞得焦头烂额的 React 单页应用(SPA)试了一下,Firecrawl 一次就返回了完整内容。原来他们在后台把无头浏览器那层给处理好了。
用法 2:用 Pydantic 做结构化提取(重头戏来了)
对于我的价格看板,我不需要 Markdown——我要的是带类型的字段,能直接加载进 pandas 的那种。Firecrawl 允许你定义一个 Pydantic 模式(schema),然后它会提取结构化数据来匹配这个模式。
from pydantic import BaseModel, Field
class ProductInfo(BaseModel):
name: str = Field(description="The product name")
price: float = Field(description="Current price in USD")
availability: str = Field(description="Stock status: In Stock, Out of Stock, etc.")
rating: float = Field(description="Average customer rating out of 5")
result = app.scrape(
'https://store.example.com/product/12345',
json_options={
"schema": ProductInfo.model_json_schema()
}
)
# 提取结构化数据
product = result['json']
print(product)
# {'name': 'Mechanical Keyboard Pro', 'price': 149.99, 'availability': 'In Stock', 'rating': 4.7}
现在我可以这么干:
import pandas as pd
# 抓取多个商品
products = []
urls = [
'https://store.example.com/product/12345',
'https://store.example.com/product/67890',
# ... 更多 URL
]
for url in urls:
result = app.scrape(url, json_options={"schema": ProductInfo.model_json_schema()})
products.append(result['json'])
df = pd.DataFrame(products)
print(df.describe()) # 瞬间得到抓取数据的统计摘要
就是在这个时刻,我才意识到 Firecrawl 是真的好用。我那个十二个网站的价格看板,从几百行手写解析器代码,变成了十二个使用同一个 Pydantic 模式的 scrape() 调用。
我踩过的坑: 一开始,我试着只用提示词(prompt)来提取,而不是用模式:
# 仅用提示词提取(简单但不太靠谱)
result = app.scrape(
'https://store.example.com/product/12345',
json_options={
"prompt": "Extract the product name, price, availability, and rating"
}
)
这样也能跑,但字段名会飘。这次抓取返回的是 {"price": 149.99},下次可能就变成 {"cost": 149.99} 了。对于需要保持列名一致的数据管线来说,一定要用 Pydantic 模式的方法。虽然每次抓取会多花 4 个点数(credits),但这可靠性绝对值回票价。
用法 3:批量抓取应对规模化需求
当我要抓取几百个商品页面时,在循环里调用 scrape() 就太慢了。Firecrawl 提供了批量操作:
# 同步批量抓取
batch_result = app.batch_scrape(urls=[
'https://store.example.com/product/12345',
'https://store.example.com/product/67890',
'https://store.example.com/product/11111',
], json_options={"schema": ProductInfo.model_json_schema()})
# 结果以列表形式返回
all_products = [r['json'] for r in batch_result]
df = pd.DataFrame(all_products)
对于更大的任务(几千个 URL),可以使用异步版本:
# 启动一个异步批量任务
job = app.start_batch_scrape(urls=large_url_list)
# 检查状态
print(job['status']) # 'processing', 'completed' 等
避坑提醒: 批量抓取是在 Firecrawl 那边并行处理的,但你仍然需要注意目标网站的限流策略。我曾经一次性发了 500 个 URL,结果被一个反爬很凶的电商网站临时封了 IP。如果你要反复抓同一个域名,记得把批次拉开点间隔。
用法 4:搜索 + 抓取,搞定数据发现
有时候你根本不知道 URL——你需要先找到它们。Firecrawl 的搜索端点可以直接返回搜索结果的完整内容:
search_results = app.search("mechanical keyboard under 150 site:amazon.com", limit=5)
# 每条结果都已经包含 markdown 内容了
for result in search_results:
print(result['markdown'][:200])
我用这个功能建了一个竞品商品描述的数据集。我不用再手动去收集 URL 了,直接搜索商品类别,一次调用就能拿回结构化的内容。
用法 5:处理交互式页面
我需要抓的一个网站,必须得点“加载更多”按钮才能看到所有商品。Firecrawl 的 actions 参数就是干这个的:
result = app.scrape(
'https://store.example.com/products',
actions=[
{"type": "wait", "milliseconds": 2000},
{"type": "click", "selector": "button.load-more"},
{"type": "wait", "milliseconds": 3000},
]
)
你可以把点击、输入、等待这些操作串起来,甚至可以在每一步截图。这直接替代了我之前写的一整套 Playwright 脚本。
费用须知
关于点数(credits),我真希望一开始就有人告诉我这些:
- 基础抓取:1 个点数
- JSON 提取:+4 个点数(共 5 个)
- 增强代理(针对难搞的网站):+4 个点数
- PDF 解析:每页 +1 个点数
- 音频提取:+4 个点数
我的价格看板每天要抓 200 个商品并做 JSON 提取,算下来每天大概要 1000 个点数。免费额度每个月才给 500 个点数,所以只要是稍微正经点的数据科学工作量,你都得买个付费套餐。提前做好预算。
一线实战经验
一定要在本地缓存结果。 网页数据不是每分钟都在变的。把抓取结果存成 JSON 文件或者 SQLite 数据库,这样开发的时候就不用反复抓同一个页面了。
抓大站前先用
map。 Firecrawl 的/map端点能列出域名下的所有 URL。先跑这个,筛出你真正需要的 URL,然后再只对那些 URL 做批量抓取。这比全站爬取要快得多,也便宜得多。提取数据后立刻做校验。 Pydantic 模式能强制校验数据类型,但抓不住语义错误。我就遇到过商品“价格”返回 0.0 的情况,因为网站上写的是“加入购物车查看价格”。提取之后一定要加上业务逻辑的校验。
优雅地处理失败。 有些网站无论如何都会封爬虫。把你的抓取调用包在 try/except 里,记录下失败情况,这样你才能知道哪些网站需要开启增强代理选项。
截图在 debug 时被严重低估了。 当提取结果很诡异时,截个图看看 Firecrawl 实际上看到的是什么:
result = app.scrape('https://problematic-site.com', screenshot=True)
# result['screenshot'] 里面是 base64 编码的图片
实话实说的局限性
Firecrawl 也不是完美的。需要登录的网站必须用 actions 工作流,但这玩意儿比我想要的要脆弱——我有两个网站不得不去调试选择器的问题。增强代理也不是对所有的反爬系统都管用。而且,如果你需要真正大规模地抓取(几百万个页面),最好还是用混合方案:用 Firecrawl 做结构化提取,用 Scrapy 这类工具做高吞吐的原始爬取。
不过,对于大多数数据科学场景——构建数据集、监控竞品、给 RAG 管线喂数据——Firecrawl 确实省去了那些爬虫的样板代码,让你能把精力集中在真正的分析上。我的价格看板从一个让人头疼三天的烂摊子,变成了一个 50 行代码的脚本。这笔买卖我绝对愿意再做一次。