Langflow lfx-firecrawl 扩展包详解:Firecrawl Scrape、Crawl、Map、Search 四个组件的安装、开发与迁移
本文围绕 Langflow 仓库中的 lfx-firecrawl 独立扩展包(Extension Bundle)展开,讲清楚它的安装与注册机制、四个 Firecrawl API 组件(Scrape、Crawl、Map、Search)的输入/输出契约与默认参数、面向 firecrawl-py v2 SDK 的参数适配层,以及旧版流程(flow)的自动迁移机制。读完后你可以直接在 Langflow 服务器中启用该扩展包,把网页抓取、整站爬取、站点地图枚举和网络搜索接入到自己的工作流或 Agent 工具中,并能看懂组件源码中 v1→v2 SDK 的关键字映射逻辑。
一、扩展包定位:从内建组件到独立 Bundle
Firecrawl 是 Langflow 工作流里用于"网页转结构化数据"的外部服务:把 URL 抓取成 Markdown/JSON、把整个站点爬取成数据集、枚举站点链接、或直接跑一次带位置偏置的网络搜索。这四个能力在 Langflow 早期是内建组件(位于 lfx.components.firecrawl.*),如今被抽取成独立分发的扩展包 lfx-firecrawl,基于 firecrawl-py v2 SDK 构建,源码位于 src/bundles/firecrawl 目录。
包的元数据定义在 pyproject.toml 中,关键事实如下:
- 包名
lfx-firecrawl,当前版本0.1.1,要求 Python>=3.10,<3.15,MIT 协议; - 运行依赖只有两项:
lfx>=1.12.0.dev0,<2.0.0(提供 BUNDLE_API 组件基类与输入/输出定义)和firecrawl-py>=4.0.0,<5.0.0(v2 版 SDK); - 通过
[project.entry-points."langflow.extensions"]声明lfx-firecrawl = "lfx_firecrawl"——这就是 README 中"通过langflow.extensionsentry-point 自动注册"的具体落点; - 构建系统为 hatchling,
[tool.hatch.build.targets.wheel]显式打包src/lfx_firecrawl包以及extension.json和components/**/*.py,保证importlib.metadata.files()能定位到清单文件。
运行时清单是 extension.json,它声明了扩展包 ID lfx-firecrawl、lfx.compat 兼容域(["1"])以及一个名为 firecrawl 的 bundle,路径指向 components/firecrawl。加载器据此把组件注册到命名空间 ID ext:firecrawl:<Class>@official 下——这一点在包入口 init.py 的模块 docstring 中也有明确说明。
{
"$schema": "https://schemas.langflow.org/extension/v1.json",
"id": "lfx-firecrawl",
"version": "0.1.1",
"name": "Firecrawl",
"description": "Firecrawl components (Scrape, Crawl, Map, and Search APIs) as a standalone Langflow Extension Bundle.",
"lfx": {
"compat": ["1"]
},
"bundles": [
{
"name": "firecrawl",
"path": "components/firecrawl"
}
]
}
二、安装与启用
按 README 的 Install 一节,安装只需一条命令:
pip install lfx-firecrawl
由于 Bundle 通过 langflow.extensions entry-point 自动注册,安装后需要重启 Langflow 服务器;重启后四个组件会出现在组件面板(palette)的 firecrawl 分组下。这里可以补充一个实现细节:pyproject.toml 中针对 editable 安装留了兜底逻辑——如果开发态安装只暴露 dist-info 条目,加载器会回退到 entry-point 去找清单,因此本地 pip install -e . 同样能被正常发现。
三、四个组件的输入输出契约
包入口 init.py 导出四个公开类:FirecrawlScrapeApi、FirecrawlCrawlApi、FirecrawlMapApi、FirecrawlSearchApi,它们分别位于 components/firecrawl/ 下的四个模块中。四个组件共享一个公共模式:
- 都继承自
lfx.custom.custom_component.component.Component; - 都带一个必填的
api_key(SecretStrInput,password=True,以密码形式在 UI 中脱敏展示); - 都在方法体内惰性
from firecrawl import Firecrawl导入 SDK,导入失败时抛出带提示信息的ImportError(提示用pip install firecrawl-py安装); - 都把 SDK 返回的 v2 类型化对象(Typed Object)通过
model_dump()序列化为 dict,包进lfx.schema.data.Data输出,供下游组件消费; - 关键入参(URL、Query 等)都标记了
tool_mode=True,意味着这些组件可以被当作 Agent 的工具调用,且这些字段会暴露为工具参数。
下面逐个拆解。
3.1 Firecrawl Scrape API(单页抓取)
实现见 firecrawl_scrape_api.py,组件展示名 "Firecrawl Scrape API",描述为 "Scrapes a URL and returns the results."。
| 输入名 | 展示名 | 类型 | 说明 |
|---|---|---|---|
api_key |
Firecrawl API Key | SecretStrInput,必填 | 调用 Firecrawl API 的密钥 |
url |
URL | MultilineInput,必填,tool_mode | 要抓取的 URL |
timeout |
Timeout | IntInput | 请求超时,单位毫秒 |
scrapeOptions |
Scrape Options | DataInput | 随请求发送的页面选项(JSON 结构) |
extractorOptions |
Extractor Options | DataInput | 结构化抽取选项 |
执行逻辑(scrape 方法)有三个值得注意的默认值处理:
formats默认设为["markdown"],即默认输出 Markdown 格式;onlyMainContent默认为True,只抓取主体内容;timeout仅在你填了值时才注入参数。
v2 SDK 的一个重要变化体现在结构化抽取上:v1 时代抽取是独立选项,v2 则在 scrape 调用里通过 formats 列表中追加一个 {"type": "json", ...} 条目来请求结构化输出。源码中的处理是:若 extractorOptions 非空,就把它转换后合并进 formats。最终调用 app.scrape(self.url, **kwargs),v2 返回类型化 Document 对象,再 model_dump() 成 dict 输出为 JSON(data)。
3.2 Firecrawl Crawl API(整站爬取)
| 输入名 | 展示名 | 类型 | 说明 |
|---|---|---|---|
api_key |
Firecrawl API Key | SecretStrInput,必填 | 密钥 |
url |
URL | MultilineInput,必填,tool_mode | 要爬取的起始 URL |
timeout |
Timeout | IntInput | 请求超时(毫秒) |
idempotency_key |
Idempotency Key | StrInput | 可选幂等键,保证请求唯一性 |
crawlerOptions |
Crawler Options | DataInput | 爬取选项 |
scrapeOptions |
Scrape Options | DataInput | 页面抓取选项 |
crawl 方法内置了一组默认值,这是该组件"开箱即用"行为的来源:
params.setdefault("maxDepth", 2) # 默认爬 2 层
params.setdefault("limit", 10000) # 默认最多 1 万个 URL
params.setdefault("allowExternalLinks", False) # 默认不爬站外链接
params.setdefault("allowBackwardLinks", False) # 默认不反向爬取
params.setdefault("ignoreQueryParameters", False) # 默认不忽略 query 参数
scrape_options_dict.setdefault("onlyMainContent", True)
随后进入 v1→v2 的关键字重命名(详见第四节),并把 scrapeOptions 构造为 v2 的类型化 ScrapeOptions 对象挂到 scrape_options 参数上。调用 app.crawl(self.url, **kwargs) 时,v2 SDK 会轮询到任务完成并返回类型化 CrawlJob 对象,组件再以 {"results": crawl_job.model_dump()} 的形式输出 JSON(data)。
3.3 Firecrawl Map API(站点链接枚举)
实现见 firecrawl_map_api.py。
| 输入名 | 展示名 | 类型 | 说明 |
|---|---|---|---|
api_key |
Firecrawl API Key | SecretStrInput,必填 | 密钥 |
urls |
URLs | MultilineInput,必填,tool_mode | 支持逗号或换行分隔的多个 URL |
ignore_sitemap |
Ignore Sitemap | BoolInput | 为 true 时忽略 sitemap.xml |
sitemap_only |
Sitemap Only | BoolInput | 为 true 时只返回 sitemap 中的链接 |
include_subdomains |
Include Subdomains | BoolInput | 为 true 时同时扫描子域名 |
map 方法的处理流程:
- 校验
urls非空,并把换行统一替换为逗号后逐段 strip,得到 URL 列表(即"逗号或换行分隔"的解析规则在源码中可验证); - sitemap 处理体现了 v2 的枚举化改造:v1 的两个独立布尔开关
ignoreSitemap/sitemapOnly在 v2 中合并为单一sitemap模式参数——sitemap_only=True映射为sitemap="only",ignore_sitemap=True映射为sitemap="skip"(sitemap_only优先级更高),两者都为 false 时不传该参数,使用默认混合行为; - 对每个 URL 调用
app.map(url, **kwargs),v2 返回类型化MapData对象,其.links是类型化链接列表;组件把各 URL 的结果links合并成一个combined_links,最终输出{"success": True, "links": combined_links}形式的JSON(data)。
3.4 Firecrawl Search API(网络搜索)
| 输入名 | 展示名 | 类型 | 说明 |
|---|---|---|---|
api_key |
Firecrawl API Key | SecretStrInput,必填 | 密钥 |
query |
Query | MultilineInput,必填,tool_mode | 搜索查询词 |
limit |
Limit | IntInput,默认 5 | 最多返回的结果数 |
location |
Location | StrInput,advanced | 搜索位置偏置(如国家/地区) |
search 方法先校验 query 非空,再把 limit、location 按需注入 kwargs 调用 app.search(self.query, **kwargs)。v2 返回按来源分组的类型化 SearchData 对象,组件 model_dump() 后直接作为 JSON(data)输出。注意 location 被标为 advanced=True,即默认折叠在高级选项里,limit 有内置默认值 5。
四、v1 → v2 SDK 适配层:camelCase 到 snake_case 的翻译
四个组件共享的核心适配逻辑是一个正则驱动的关键字转换器(见 Scrape 模块 L13-L23 与 Crawl 模块中的同名定义):
_CAMEL_TO_SNAKE_RE = re.compile(r"(?<!^)(?=[A-Z])")
def _to_snake_case_kwargs(params: dict) -> dict:
return {_CAMEL_TO_SNAKE_RE.sub("_", key).lower(): value for key, value in params.items()}
背景是:Firecrawl v1 API/SDK 惯例使用 camelCase 参数名,而 v2 SDK 的 Python 接口要求 snake_case 关键字参数。组件允许用户以 v1 风格(camelCase)填写 options 数据,转换器统一转成 snake_case;已经是 snake_case 的键原样通过。
在 Crawl 组件中,光做大小写转换还不够,v2 还对部分选项做了改名和语义合并,源码给出了明确的映射(firecrawl_crawl_api.py):
| v1 参数 | v2 参数 | 说明 |
|---|---|---|
maxDepth |
max_discovery_depth |
改名 |
allowBackwardLinks |
crawl_entire_domain |
改名 |
ignoreSitemap(bool) |
sitemap="skip" |
v2 移除了该布尔参数,改为 sitemap 模式枚举 |
Map 组件中同样的枚举化思想体现为 sitemap_only → sitemap="only"、ignore_sitemap → sitemap="skip"。这套"默认值 + 重命名 + 枚举映射"的适配层,正是该 Bundle "built against the firecrawl-py v2 SDK" 的工程实质。
五、本地开发与验证
README 的 Develop 一节给出的开发流程是:
cd src/bundles/firecrawl
pip install -e .
lfx extension validate src/lfx_firecrawl
即先以 editable 方式安装 Bundle,再用 lfx extension validate 对扩展目录做校验。结合 pyproject.toml 的 hatch 打包配置可以知道:editable 安装时组件与 extension.json 都能被加载器发现(wheel 与 editable 双通道兜底),因此本地改动组件代码后重启服务器即可生效。
六、旧流程迁移:迁移表如何兜底存量 Flow
README 的 Migration 一节说明:引用了旧类名或旧导入路径 lfx.components.firecrawl.* 的已保存 Flow,会由迁移表自动改写为新的命名空间 ID。迁移表位于 migration_table.json,其中针对 Firecrawl 的条目覆盖了三类旧引用,例如 Crawl 组件(表内 L836-L851 一带):
- 旧导入路径
lfx.components.firecrawl.firecrawl_crawl_api.FirecrawlCrawlApi→ 目标ext:firecrawl:FirecrawlCrawlApi@official; - 包级旧导入路径
lfx.components.firecrawl.FirecrawlCrawlApi→ 目标ext:firecrawl:FirecrawlCrawlApi@official; - 旧槽位
ext:firecrawl:FirecrawlCrawlApi@official-pre-a→ 目标ext:firecrawl:FirecrawlCrawlApi@official。
Map、Scrape、Search 三个组件各有同构的三条映射(分别对应表内 L856-L871、L876-L891、L896-L911 一带)。这也解释了 Bundle 内部的一个"看似冗余"的设计:components/firecrawl/init.py 的 docstring 明确指出,它按名重导出四个组件类,是为了让迁移表中指向 lfx.components.firecrawl.<Class> 的旧条目仍然可以按名解析到移动后的组件类。对用户的实际影响是:升级后打开老 Flow,节点引用会被自动重写到 ext:firecrawl:*@official 新 ID,不需要手工改 JSON。
七、测试如何锁定 v2 行为
Bundle 自带单测 tests/test_firecrawl_components.py。测试策略值得注意:不访问网络、不要求真实安装 firecrawl-py,而是通过 sys.modules 注入伪造的 firecrawl、firecrawl.v2、firecrawl.v2.types 模块(fixture mock_firecrawl),让组件方法内的惰性导入直接解析到 mock 客户端。关键断言包括:
- Scrape:客户端以
api_key构造、scrape收到 URL 位置参数,返回的model_dump()结果原样进入result.data;SDK 异常(如RuntimeError)会向上透传; - Crawl:断言 kwargs 中出现
max_discovery_depth且不含max_depth、出现crawl_entire_domain,直接锁死了第四节的 v1→v2 重命名逻辑;输出为result.data["results"]包裹的任务对象; - Map:
sitemap_only=True时断言sitemap == "only",ignore_sitemap=True时断言sitemap == "skip";多 URL 的links被合并进result.data["links"]; - Search:
query为位置参数,limit/location为关键字参数,SearchData序列化结果直接输出。
这套测试与组件源码一一对应,也说明了该 Bundle 的行为契约(默认值、重命名、序列化格式)是被测试显式固化的。
八、小结
lfx-firecrawl 是 Langflow 扩展体系下"官方组件 → 独立 Bundle"演进的典型样本:
- 分发与注册:
pyproject.toml的langflow.extensionsentry-point +extension.json清单,pip install lfx-firecrawl后重启即得firecrawl分组组件; - 能力面:Scrape(单页抓取,默认 Markdown + 仅主体内容)、Crawl(整站爬取,默认 2 层深度、1 万 URL 上限)、Map(多 URL 链接枚举,sitemap 枚举化)、Search(网络搜索,默认 5 条结果)四个组件,均以 JSON Data 输出,且关键入参支持 Agent 工具模式;
- 工程细节:camelCase→snake_case 正则转换加上
maxDepth→max_discovery_depth、allowBackwardLinks→crawl_entire_domain、sitemap 布尔开关→枚举模式的重命名适配层,是 firecrawl-py v2 兼容的核心; - 迁移保障:
migration_table.json将旧lfx.components.firecrawl.*引用自动重写到ext:firecrawl:*@official,存量 Flow 无需手工修复。
如果你要在 Langflow 中搭建"网页数据 → Agent 上下文"的链路,这个 Bundle 提供的四个组件已经覆盖了从单页抓取到整站爬取、从站点结构枚举到联网搜索的完整入口,且行为都有对应单测约束,可以放心作为工作流的稳定数据源组件使用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00