首页
/ Crawl4AI C4A-Script 实战指南:用类英文 DSL 编写网页自动化脚本及其编译器实现解析

Crawl4AI C4A-Script 实战指南:用类英文 DSL 编写网页自动化脚本及其编译器实现解析

2026-09-06 18:03:18作者:侯霆垣

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.activeElementvalue 并派发 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 替换发生在编译期,且只对 TYPEEVALSET 三类命令的参数生效,未定义的变量会被原样保留。变量在整个脚本(含 PROC 过程体)中共享作用域,值始终为字符串。

四、命令全表:六大类别

以下表格完整继承自官方文档,并按 语法文件 补充了默认值。

4.1 导航命令

命令 用途 示例
GO 导航到 URL GO https://example.com
RELOAD 刷新当前页 RELOAD
BACK 后退 BACK
FORWARD 前进 FORWARD

4.2 等待命令

命令 用途 示例
WAIT 等待时间/元素/文本 WAIT 3WAIT \#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 TabPRESS Enter
CLEAR 清空输入框 CLEAR \#search``
SET 直接设置输入值 SET \#email` "user@example.com"`

语法中另有 KEY_DOWN / KEY_UP 用于按住/释放修饰键(如 KEY_DOWN Shift + KEY_UP Shift 组合键),需成对使用。CLEARSET 的区别在于:SET 会先清空再赋值并聚焦,同时派发 inputchange 事件(见 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.c4amulti_step_workflow.c4aadvanced_control_flow.c4adata_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 主流程):

  1. _parse_with_includes:解析文本并展开 USE 包含,同时用 seen 集合检测循环包含(抛出 Circular include);
  2. _collect_procs:收集所有 PROC 定义到 self.procs
  3. _inline_calls:将过程调用内联展开为命令序列——未定义的过程会抛出 Unknown procedure(对应外层错误码 E005);
  4. _apply_set_vars:按顺序执行 SETVAR 并对 TYPE/EVAL/SET$var 文本替换,最后逐条 Cmd 生成 JS(NOP 注释被剔除)。

6.3 生成 JavaScript 的关键手法

  • 点击:先 focus() 再派发 bubbles: trueMouseEventCLICK/DBLCLICK/RIGHTCLICK 分别映射到 click/dblclick/contextmenu 事件、不同 detail 值);
  • 滚动SCROLL 转译为 window.scrollBy(dx, dy)
  • 按键PRESS 依次派发 keydownkeyup 两个 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 元数据),失败时把异常转换为结构化 ErrorDetailcompile 实现);
  • 错误码体系定义在 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_scriptjs_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.pygithub_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.c4a05-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 的价值在于把"页面交互"从脆弱的 JS 片段升格为可审查、可复用、可视觉化维护的脚本资产:开发者用它写 UI 测试,演示者用它做产品 Demo,运营同学用它录入数据——而 Crawl4AI 的抓取管线(Markdown 生成、链接提取、截图、LLM 抽取)始终运行在这些交互之后,两者通过 js_code 这一条管道无缝衔接。

登录后查看全文
热门项目推荐
相关项目推荐