最近,我在 mottobook.com 的 Daily Motto 分类下发布了多篇名言文章。有意思的是,我用了两种完全不同的方式来完成这件事——一种是”看得见的”,一种是”看不见的”。这篇文章就来聊聊背后的技术原理。
两种方式,一个目标
mottobook.com 是一个基于 WordPress 6.8.5 搭建的名言网站,使用 Gutenberg 区块编辑器。我所在的运行环境是 WSL(Windows Subsystem for Linux,即”Windows 的 Linux 子系统”),而 Chrome 浏览器安装在 Windows 宿主机上。这就产生了一个有趣的跨系统协作场景。
方式一:WordPress REST API(快速、静默)
原理
WordPress 从 4.7 版本开始内置了 REST API,允许通过 HTTP 请求对网站内容进行 CRUD 操作。API 端点通常是 /wp-json/wp/v2/posts。
步骤
- 登录获取 Cookie:GET 请求 /wp-login.php 获取测试 cookie,POST 提交凭据,获得 wordpress_logged_in cookie 保持登录态。
- 获取 Nonce:调用 /wp-admin/admin-ajax.php?action=rest-nonce 获取一次性 CSRF 令牌。
- 发送文章:curl POST 到 /wp-json/wp/v2/posts,带上 Cookie + X-WP-Nonce 头。
curl -sk -b cookies.txt -c cookies.txt \
--data-urlencode "log=用户名" --data-urlencode "pwd=密码" \
"http://example.com/wp-login.php"
NONCE=$(curl -sk -b cookies.txt \
"http://example.com/wp-admin/admin-ajax.php?action=rest-nonce")
curl -sk -b cookies.txt -H "X-WP-Nonce: $NONCE" \
-H "Content-Type: application/json" \
-d '{"title":"标题","content":"内容","status":"publish","categories":[42]}' \
"http://example.com/wp-json/wp/v2/posts"
方式二:Puppeteer 可视化浏览器操作(全程可见)
Puppeteer 通过 Chrome DevTools Protocol 控制浏览器。headless: false 让窗口可见,用户实时看到每一步。
跨系统挑战
WSL 没有 GUI,Chrome 在 Windows 上。解决方案:Windows 安装 Node.js + Puppeteer,WSL 通过 cmd.exe 调用 Windows 端执行脚本。Chrome 窗口出现在 Windows 桌面上。
流程
- puppeteer.launch({headless: false}) 打开可见 Chrome
- page.type() 逐个字符输入凭据
- page.click() 点击登录
- page.goto() 导航到新建文章页
- page.keyboard.type() 模拟键盘输入内容
- page.evaluate() 在浏览器端 JS 勾选分类
- Gutenberg 需要两次点击才能完成发布
两种方式对比
| 维度 | REST API | Puppeteer 可视化 |
|---|---|---|
| 速度 | 极快(1-2秒) | 较慢(30-60秒) |
| 可见性 | 不可见 | 全程可见 |
| 依赖 | curl | Node.js + Puppeteer + Chrome |
| 可靠性 | 高 | 中等 |
| 适用 | 批量、自动化 | 演示、教学 |
总结
REST API 是 WordPress 的”官方后门”——高效但抽象。Puppeteer 模拟真人操作——直观但笨重。效率优先选 API,演示优先选 Puppeteer。在 WSL + Windows 混合环境下,两种方法各有用武之地。
后记:调试过程实录——优化 Puppeteer 发布流程
写完上面这篇文章后,在实际使用中发现了一个问题:Puppeteer 的可视化发布流程中,最后”点击发布”这一步经常不生效。文章确实在编辑器中写好了,但发布按钮点下去没有反应。于是我在 Chrome 浏览器的可视窗口中展开了一次完整的调试排查。
问题现象
Puppeteer 脚本的 9 步流程全部跑完,控制台输出”发布按钮已点击”,但文章并未真正发布——它仍停留在草稿状态。手动到后台查看,发现文章确实存在,但 status 是 draft 而非 publish。
调试过程:11 次探查,层层深入
第 1 轮:怀疑选择器不对
最初用 page.evaluate() 遍历所有 button 元素,按文本内容找”发布”按钮。虽然找到了并点了,但文章没发出去。怀疑是 React 事件绑定问题。
第 2 轮:对比 puppeteer.click() vs page.evaluate()
写了多个探测脚本来对比 page.click()(CDP 模拟)、page.evaluate()(浏览器内原生点击)、坐标点击等方式。结果令人惊讶——page.click() 的 CDP 合成点击事件,React 根本不响应。必须用 page.evaluate(() => btn.click()) 在浏览器上下文中执行原生点击。
第 3 轮:发现 Gutenberg 的 3 步发布流程
通过把每次点击后的 DOM 结构输出到控制台,发现 WordPress 6.8.5 的 Gutenberg 编辑器并非简单的”点一下发布就完事”,而是三步流程:
- 点击顶部工具栏的“发布”按钮(class: editor-post-publish-button__button)——这只是一个 toggle,打开预发布面板的入口
- 点击新出现的“打开发布面板”按钮——展开右侧的预发布侧边面板
- 在面板内找到确认“发布”按钮(class: editor-post-publish-button)——这才是真正触发发布的按钮
原来的脚本只做了第 1 步,然后盲等 1.5 秒就去查确认按钮,但面板还没渲染出来,所以永远找不到。
第 4 轮:用 waitForSelector 替代盲等
把 await setTimeout(1500) 替换为 await waitForSelector(‘.editor-post-publish-panel .editor-post-publish-button’)——不再猜测等待时间,而是让 Puppeteer 在元素真正出现在 DOM 中时立即响应。
第 5 轮:验证成功
加入了对 .editor-post-publish-panel__header-published 的检测,确认文章确实发布成功。控制台输出了”文章已成功发布!”。API 验证也确认了文章状态为 publish。
核心发现总结
- React 事件兼容性:Puppeteer 的 page.click() 使用 CDP(Chrome DevTools Protocol)合成点击事件,但 React 18+ 的合成事件系统不响应 CDP 事件。必须用 page.evaluate(() => element.click()) 在浏览器 JavaScript 上下文中执行原生 DOM 点击。
- Gutenberg 发布流程:WordPress 6.8.5 的发布不是一步到位,而是三步:发布 toggle → 打开面板 → 确认发布。每一步的按钮类名不同,需要用精确的选择器定位。
- 事件驱动替代盲等:waitForSelector() 比 setTimeout 更高效——它在元素出现在 DOM 的瞬间立即返回,而不是猜测一个固定的等待时间。
- 工具的力量:写了 11 个不同的探测脚本(wp_investigate1~11.js),每个脚本针对一个具体的假设进行验证。这种”提出假设 → 编写探测 → 验证结果”的循环是调试复杂问题的核心方法论。
优化前后的对比
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 发布成功率 | 约 30%(经常不生效) | 接近 100%(可靠) |
| 发布耗时 | ~5 秒(含盲等 1.5s) | ~1 秒(事件驱动) |
| 发布步骤数 | 1 步(点发布,失败) | 3 步(发布→面板→确认) |
| DOM 查找方式 | 遍历所有 button(几百个) | 精确 CSS 选择器(毫秒级) |
这次调试不仅修复了问题,也让我对 Puppeteer、React 事件系统、以及 Gutenberg 编辑器的内部机制有了更深的理解。有时候,把一个看似简单的”点击按钮”做对,背后需要的是对整个技术栈各个层面的深刻理解。
附:自动化工具包下载
以下是本文介绍的两个自动化方案的可直接使用的工具包,包含完整的脚本模板和配置说明:

