Crawl4AI C4A-Script 实战指南:用类英文 DSL 编写网页自动化脚本及其编译器实现解析
C4A-Script 是 Crawl4AI 内置的一套"类英文"领域特定语言(DSL),用于描述浏览器中的点击、输入、滚动、条件判断等交互动作。读完本文,你能用几行 GO / WAIT / CLICK / TYPE 命令写出可执行的网页自动化脚本,理解它如何被 Crawl4AI 的编译器转译为 JavaScript 注入页面,并能将脚本无缝接入 CrawlerRunConfig 完成"先交互、再抓取"的动态页面采集。
一、什么是 C4A-Script:定位与设计目标
C4A-Script 是一门面向网页自动化与人机交互的、人类可读的 DSL。它的核心卖点可以概括为"简单语法 + 强大结果":用接近自然语言的单行命令即可完成完整的用户操作流。官方文档 核心介绍 给出的最小示例如下:
# Navigate and interact in plain English
GO https://example.com
WAIT `#search-box` 5
TYPE "Hello World"
CLICK `button[type="submit"]`
四行脚本就完成了"打开页面 → 等待搜索框 → 输入文本 → 点击提交"的完整流程。在 Crawl4AI 仓库中,C4A-Script 的实现集中在 script 包:词法/语法解析与 JS 代码生成在 c4ai_script.py,基于 Result 模式的外层编译 API 在 c4a_compile.py,编译结果与错误数据结构在 c4a_result.py。
它特别适合以下场景(均继承自官方文档的适用性说明):
- UI 测试:自动化用户交互流程;
- 演示制作:构建可交互的产品演示;
- 数据录入:自动化表单填写与提交;
- 流程验证:校验复杂用户旅程;
- 教学培训:无需编程基础即可学习网页自动化。
此外,C4A-Script 还提供基于 Google Blockly 的可视化编辑器,支持拖拽积木块生成脚本,视觉模式与文本脚本实时双向同步——对非程序员和快速原型验证非常友好。
二、第一个脚本:从零到可运行
官方入门脚本演示了在 DuckDuckGo 上完成一次完整搜索:
# My first C4A-Script
GO https://duckduckgo.com
# Wait for the search box to appear
WAIT `input[name="q"]` 10
# Type our search query
TYPE "Crawl4AI"
# Press Enter to search
PRESS Enter
# Wait for results
WAIT `.results` 5
逐行解读(结合 c4ai_script.py 中 WAIT 的 JS 生成逻辑):
| 命令 | 语义 | 编译器实际生成的行为 |
|---|---|---|
GO https://duckduckgo.com |
导航到 URL | window.location.href = '...' |
WAIT input[name="q"] 10 |
等待元素出现,最多 10 秒 | 每 100ms 轮询 document.querySelector,超时则抛出 WAIT selector timeout |
TYPE "Crawl4AI" |
向当前聚焦元素追加文本 | 向 document.activeElement 写 value 并派发 input 事件 |
PRESS Enter |
按 Enter 键 | 派发 keydown + keyup 两个 KeyboardEvent |
WAIT .results 5 |
等待结果区出现 | 同上,元素出现即返回 |
可以看到,WAIT <selector> <timeout> 不是"盲等",而是源码中生成了一段带轮询与超时 reject 的 Promise——这也是官方最佳实践中反复强调"先 WAIT 再操作"的原因。
三、核心语法:命令、选择器与变量
3.1 命令与语法基础
C4A-Script 使用类英文命令,每条命令只做一件事:
# Comments start with #
COMMAND parameter1 parameter2
# Most commands use CSS selectors in backticks
CLICK `#submit-button`
# Text content goes in quotes
TYPE "Hello, World!"
# Numbers are used directly
WAIT 3
从 源码语法定义 可以确认三种"字面量"约定:CSS 选择器与任意 JavaScript 片段用反引号 `...` 包裹(BACKTICK_STRING),纯文本用双引号(ESCAPED_STRING),数字直接书写(NUMBER)。注释以 # 开头。
3.2 选择器:如何定位元素
C4A-Script 直接使用 CSS 选择器定位元素:
# By ID
CLICK `#login-button`
# By class
CLICK `.submit-btn`
# By attribute
CLICK `button[type="submit"]`
# By accessible attributes
CLICK `button[aria-label="Search"][title="Search"]`
# Complex selectors
CLICK `.form-container input[name="email"]`
3.3 变量与动态内容
SETVAR 定义变量,$ 前缀引用变量值:
# Set a variable
SETVAR username = "john@example.com"
SETVAR password = "secret123"
# Use variables (prefix with $)
TYPE $username
PRESS Tab
TYPE $password
从 编译器的变量替换通道 _apply_set_vars 可以看到实现细节:$var 替换发生在编译期,且只对 TYPE、EVAL、SET 三类命令的参数生效,未定义的变量会被原样保留。变量在整个脚本(含 PROC 过程体)中共享作用域,值始终为字符串。
四、命令全表:六大类别
以下表格完整继承自官方文档,并按 语法文件 补充了默认值。
4.1 导航命令
| 命令 | 用途 | 示例 |
|---|---|---|
GO |
导航到 URL | GO https://example.com |
RELOAD |
刷新当前页 | RELOAD |
BACK |
后退 | BACK |
FORWARD |
前进 | FORWARD |
4.2 等待命令
| 命令 | 用途 | 示例 |
|---|---|---|
WAIT |
等待时间/元素/文本 | WAIT 3 或 WAIT \#element` 10` |
WAIT 是三类语义的合写,编译器会按实参类型区分(见 wait_cmd 转换逻辑):
WAIT 3:固定等待 3 秒(支持小数,如WAIT 1.5);WAIT \#el` 10`:等待元素出现,超时(未写则默认 10 秒)报错;WAIT "Some text" 10:等待页面文本出现,大小写敏感。
4.3 鼠标命令
| 命令 | 用途 | 示例 |
|---|---|---|
CLICK |
点击元素或坐标 | CLICK \button`或CLICK 100 200` |
DOUBLE_CLICK |
双击元素 | DOUBLE_CLICK \.item`` |
RIGHT_CLICK |
右键元素 | RIGHT_CLICK \#menu`` |
SCROLL |
按方向滚动 | SCROLL DOWN 500 |
DRAG |
从一点拖拽到另一点 | DRAG 100 100 500 300 |
语法中还定义了 MOVE <x> <y>(移动光标、触发 hover,见 SCROLL/MOVE/DRAG 的 JS 生成)。SCROLL 未写距离时默认 500 像素;CLICK 100 200 这类坐标点击会通过 document.elementFromPoint 命中元素再派发事件。
4.4 键盘命令
| 命令 | 用途 | 示例 |
|---|---|---|
TYPE |
输入文本或变量 | TYPE "Hello" 或 TYPE $username |
PRESS |
按特殊键 | PRESS Tab 或 PRESS Enter |
CLEAR |
清空输入框 | CLEAR \#search`` |
SET |
直接设置输入值 | SET \#email` "user@example.com"` |
语法中另有 KEY_DOWN / KEY_UP 用于按住/释放修饰键(如 KEY_DOWN Shift + KEY_UP Shift 组合键),需成对使用。CLEAR 与 SET 的区别在于:SET 会先清空再赋值并聚焦,同时派发 input 与 change 事件(见 SET 的 JS 生成),对 React/Vue 等依赖事件监听的框架表单更可靠。
4.5 控制流
| 命令 | 用途 | 示例 |
|---|---|---|
IF |
条件执行 | IF (EXISTS \#popup`) THEN CLICK `#close`` |
REPEAT |
循环命令 | REPEAT (SCROLL DOWN 300, 5) |
从 IF 命令的语法 看,条件支持三种形式:EXISTS \selector`、NOT EXISTS `selector`,以及直接写 JS 表达式的反引号条件;IF支持可选的ELSE` 分支,且条件成立与否时各执行一条命令:
IF (EXISTS `.user-menu`) THEN CLICK `.logout` ELSE CLICK `.login`
IF (`window.innerWidth < 768`) THEN CLICK `.mobile-menu`
REPEAT 的计数既可以是数字,也可以是 JS 表达式(用于"满足条件才重复"的模式)。
4.6 变量与高级命令
| 命令 | 用途 | 示例 |
|---|---|---|
SETVAR |
创建变量 | SETVAR email = "test@example.com" |
EVAL |
执行 JavaScript | EVAL \console.log('Hello')`` |
EVAL 内是完整 JS,编译时会被包进一个 try/catch(见 EVAL 的 JS 生成),执行异常只打印 C4A-Script EVAL error 而不中断脚本,适合调试。此外语法还定义了:
PROC <name> ... ENDPROC:定义可复用的命令序列("过程"),后续直接以过程名调用;USE "path.c4a":包含其他脚本文件,编译期会做循环包含检测(includes 解析)。
五、真实场景脚本示例
以下三个示例完整继承自官方文档,覆盖了登录流、电商购物与带条件分支的表单自动化。
示例 1:登录流程
# Complete login automation
GO https://myapp.com/login
# Wait for page to load
WAIT `#login-form` 5
# Fill credentials
CLICK `#email`
TYPE "user@example.com"
PRESS Tab
TYPE "mypassword"
# Submit form
CLICK `button[type="submit"]`
# Wait for dashboard
WAIT `.dashboard` 10
示例 2:电商购物(变量 + 过滤)
# Shopping automation with variables
SETVAR product = "laptop"
SETVAR budget = "1000"
GO https://shop.example.com
WAIT `#search-box` 3
# Search for product
TYPE $product
PRESS Enter
WAIT `.product-list` 5
# Filter by price
CLICK `.price-filter`
SET `#max-price` $budget
CLICK `.apply-filters`
# Select first result
WAIT `.product-item` 3
CLICK `.product-item:first-child`
示例 3:带条件分支的表单自动化
# Smart form filling with error handling
GO https://forms.example.com
# Check if user is already logged in
IF (EXISTS `.user-menu`) THEN GO https://forms.example.com/new
IF (NOT EXISTS `.user-menu`) THEN CLICK `#login-link`
# Fill form
WAIT `#contact-form` 5
SET `#name` "John Doe"
SET `#email` "john@example.com"
SET `#message` "Hello from C4A-Script!"
# Handle popup if it appears
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept-cookies`
# Submit
CLICK `#submit-button`
WAIT `.success-message` 10
仓库中还提供了可直接参考的样本脚本目录 script_samples,包含 login_flow.c4a、multi_step_workflow.c4a、advanced_control_flow.c4a、data_extraction.c4a 等 15 个 .c4a 样本,其中 login_flow.c4a 演示了 PROC 过程定义、变量注入与登录后状态校验的组合用法。
六、编译器深度解析:从文本到 JavaScript
C4A-Script 的执行链路是:文本 → Lark 语法解析(AST)→ Transformer 转 IR(Cmd/Proc)→ 四个编译 Pass → JavaScript 语句列表。核心实现全部在 c4ai_script.py。
6.1 基于 Lark 的语法解析
编译器使用 Lark 的 LALR 解析器加载内嵌文法(GRAMMAR 定义),每条命令映射为一个 IR 操作,例如:
# 语法(节选)
nav : "GO" URL -> go
| "RELOAD" -> reload
click : "CLICK" (BACKTICK_STRING|NUMBER NUMBER) -> click
if_cmd : "IF" "(" condition ")" "THEN" command ("ELSE" command)?
ASTBuilder(Lark 的 Transformer 子类,见 ASTBuilder)将 AST 扁平化为 Cmd(op, args) 中间表示。以 WAIT 为例,它把实参归一为 (值, 类型) 二元组,类型是 seconds / selector / text 之一——这正是第四节中"一种命令、三种语义"的实现来源。
6.2 四个编译 Pass
Compiler.compile 依次执行四个 Pass(compile 主流程):
_parse_with_includes:解析文本并展开USE包含,同时用seen集合检测循环包含(抛出Circular include);_collect_procs:收集所有PROC定义到self.procs;_inline_calls:将过程调用内联展开为命令序列——未定义的过程会抛出Unknown procedure(对应外层错误码 E005);_apply_set_vars:按顺序执行SETVAR并对TYPE/EVAL/SET做$var文本替换,最后逐条Cmd生成 JS(NOP注释被剔除)。
6.3 生成 JavaScript 的关键手法
- 点击:先
focus()再派发bubbles: true的MouseEvent(CLICK/DBLCLICK/RIGHTCLICK分别映射到click/dblclick/contextmenu事件、不同detail值); - 滚动:
SCROLL转译为window.scrollBy(dx, dy); - 按键:
PRESS依次派发keydown与keyup两个KeyboardEvent; - 等待:选择器/文本等待生成带
setInterval轮询(100ms 间隔)与超时 reject 的 Promise; - 条件与循环:
EXISTS条件转!!document.querySelector('...'),NOT递归取反,REPEAT数字计数转for循环,JS 表达式计数则先求值再决定是否执行循环体(REPEAT 生成逻辑)。
需要注意的一个实现事实:生成的 TYPE 作用于 document.activeElement,即必须先 CLICK 聚焦目标输入框,再 TYPE——这与示例中"CLICK #email 之后 TYPE"的写法一致。
6.4 Result 模式的外层 API 与错误码
面向用户的编译入口是 c4a_compile.py 中的 C4ACompiler,它采用"不抛异常、总是返回 Result"的设计:
from crawl4ai.script import compile, validate
result = compile(script) # -> CompilationResult(success, js_code, errors)
v = validate(script) # -> ValidationResult(valid, errors, warnings)
compile(script)内部调用Compiler.compile,成功时返回CompilationResult(含js_code语句列表与lineCount/statementCount元数据),失败时把异常转换为结构化ErrorDetail(compile 实现);- 错误码体系定义在 ERROR_CODES:E001 缺
THEN、E002 缺右括号、E003 缺逗号、E004 缺ENDPROC、E005 未定义过程、E006 缺反引号、E007 非法命令、E999 通用语法错误; - c4a_result.py 定义了
ErrorDetail的完整字段:行/列、源码上下文行、修复建议(Suggestion)、to_dict()/to_json()序列化与终端美化输出formatted_message,便于接入编辑器或 CI 做脚本校验。
最小使用示例可参考仓库中的 hello world 示例:
from crawl4ai.script.c4a_compile import compile
script = """
GO https://example.com
WAIT `#content` 5
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept`
CLICK `button.submit`
"""
result = compile(script)
if result.success:
for js in result.js_code: # 可直接传给 CrawlerRunConfig(js_code=...)
print(js)
else:
error = result.first_error
print(f"Line {error.line}, {error.column}: {error.message}")
print(f"Code: {error.source_line}")
七、接入 Crawl4AI:c4a_script 参数与执行时机
7.1 推荐方式:CrawlerRunConfig(c4a_script=...)
CrawlerRunConfig 提供了一等公民参数 c4a_script(参数定义),接受字符串或字符串列表。其工作流在 async_configs.py 中非常简洁:
# Compile C4A scripts if provided
if self.c4a_script and not self.js_code:
self._compile_c4a_script()
即:只要提供了 c4a_script 且未显式指定 js_code,就会在构造配置时自动编译,编译产物写入 self.js_code。_compile_c4a_script(实现)会逐个编译脚本列表中的每项,任一失败即抛出带行号、列号、源码行与修复建议的 ValueError,格式形如:
C4A Script compilation error (script 1):
Line 3, Column 10: Missing 'THEN' keyword after IF condition
Code: IF (EXISTS `.popup`) CLICK `.close`
Suggestion: Add THEN keyword
完整集成示例(修正自官方文档,采用源码实际支持的 c4a_script 参数):
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
script = """
WAIT `.content` 5
IF (EXISTS `.load-more-button`) THEN CLICK `.load-more-button`
WAIT `.additional-content` 5
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept-all`
"""
config = CrawlerRunConfig(
c4a_script=script, # C4A-Script 文本,自动编译为 js_code
wait_for=".content",
screenshot=True
)
async with AsyncWebCrawler() as crawler:
result = await crawler.arun("https://example.com", config=config)
print(result.markdown)
说明:核心文档 中的集成示例写作
js_code=script,但从 配置类源码 看,自动编译只发生在c4a_script参数上(且仅在未同时提供js_code时触发);js_code参数期望的是 JavaScript 语句。因此自动化脚本请优先使用c4a_script,js_code保留给原生 JS 注入。
7.2 脚本在页面生命周期中的执行时机
编译产物最终作为 js_code 执行。在 async_crawler_strategy.py 中,config.js_code 是在 wait_for 等待完成之后才被逐条注入页面执行的;此外还有一个更早的钩子 js_code_before_wait(执行位置),用于在等待之前触发加载动作。可以推断:C4A-Script 编译出的语句按脚本顺序串行执行,适合作为"交互前置层"——先完成点击/滚动/登录,再让 Crawl4AI 的主流程提取 Markdown 与链接。仓库中的 amazon_r2d2_search.py 与 github_search_crawler.py 展示了"脚本生成 + 交互 + 数据抽取"的完整端到端案例。
八、可视化编程与本地教程环境
C4A-Script 附带基于 Google Blockly 的可视化编程界面,面向非程序员、快速原型、教学协作三类用户。核心特性(继承自官方文档):
- 拖拽界面:连接积木块即可构建脚本;
- 实时同步:视觉模式的修改即时反映到文本脚本;
- 智能分块:积木按功能分类(导航、动作等);
- 防错设计:连接方式从结构上杜绝语法错误;
- 注释块:支持可视化注释用于文档说明。
教程环境是一个 Flask Web 应用,包含:带语法高亮的实时代码编辑器、Blockly 拖拽编辑器、录制模式(在预览中操作浏览器,实时生成 C4A-Script 命令)以及时间线视图(查看与编辑自动化步骤)。本地运行方式(与官方文档一致):
# 进入教程目录
cd docs/examples/c4a_script/tutorial/
# 安装依赖
pip install -r requirements.txt
# 启动教程服务
python server.py
# 浏览器打开 http://localhost:8000
教程目录下还有 5 个循序渐进的示例脚本 scripts/(01-basic-interaction.c4a 至 05-complex-workflow.c4a)以及一个独立的 playground 页面,可直接对照学习。录制模式的使用步骤:点击教程界面中的 "Record" → 在浏览器预览中执行操作 → 实时观察 C4A-Script 命令生成 → 编辑润色生成脚本。
九、调试手段与最佳实践
9.1 调试技巧
# Use comments for debugging
# This will wait up to 10 seconds for the element
WAIT `#slow-loading-element` 10
# Check if element exists before clicking
IF (EXISTS `#optional-button`) THEN CLICK `#optional-button`
# Use EVAL for custom debugging
EVAL `console.log("Current page title:", document.title)`
编译期排错则依赖 C4AScriptError 的可读错误格式:它会打印出错行号/列号、出错源码行与 ^ 定位标记,并针对高频错误给出上下文化提示(如"Missing 'THEN' keyword after IF condition")。
9.2 四条最佳实践(继承自官方文档)
1. 永远先等待元素再操作
# Bad: Clicking immediately
CLICK `#button`
# Good: Wait for element to appear
WAIT `#button` 5
CLICK `#button`
2. 使用描述性注释
# Login to user account
GO https://myapp.com/login
WAIT `#login-form` 5
# Enter credentials
TYPE "user@example.com"
PRESS Tab
TYPE "password123"
# Submit and wait for redirect
CLICK `#submit-button`
WAIT `.dashboard` 10
3. 处理可变条件(弹窗/状态分支)
# Handle different page states
IF (EXISTS `.cookie-banner`) THEN CLICK `.accept-cookies`
IF (EXISTS `.popup-modal`) THEN CLICK `.close-modal`
# Proceed with main workflow
CLICK `#main-action`
4. 用变量提升复用性
# Define once, use everywhere
SETVAR base_url = "https://myapp.com"
SETVAR test_email = "test@example.com"
GO $base_url/login
SET `#email` $test_email
补充一个来自源码的注意点:
$var替换仅对TYPE/EVAL/SET生效(见 替换逻辑)。若希望GO的目标 URL 也使用变量,建议先用SETVAR定义完整 URL,再在交互命令中引用,或直接在GO中写完整地址。
十、继续深入:仓库资源索引
- 完整命令参考:C4A-Script API Reference——逐命令的语法、参数与示例,包括
KEY_DOWN/KEY_UP修饰键、JS 条件式IF与条件式REPEAT; - 核心文档:c4a-script.md;
- 示例脚本库:script_samples/(15 个
.c4a样本,覆盖登录、加购、无限滚动、多步工作流等); - 端到端案例:amazon_example/ 与 github_search/——脚本生成、交互与 JSON 抽取的完整链路;
- 教程环境:tutorial/(Flask 服务 + Blockly 可视化 + 录制模式);
- 编译器源码:c4ai_script.py(文法与代码生成)、c4a_compile.py(Result API)、c4a_result.py(错误数据结构);
- 集成入口:CrawlerRunConfig.c4a_script 与 _compile_c4a_script。
C4A-Script 的价值在于把"页面交互"从脆弱的 JS 片段升格为可审查、可复用、可视觉化维护的脚本资产:开发者用它写 UI 测试,演示者用它做产品 Demo,运营同学用它录入数据——而 Crawl4AI 的抓取管线(Markdown 生成、链接提取、截图、LLM 抽取)始终运行在这些交互之后,两者通过 js_code 这一条管道无缝衔接。
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