AI 自动发布博客的乱码血泪史:Emlog + 浏览器自动化的 UTF-8 陷阱与正解

島主 发布于 阅读:42 建站运维

上一篇《Hermes Agent 安装踩坑手记》是用 AI 编程助手从安装到撰文全程自动化的。但把这篇手记发布到 Emlog 博客的过程,本身又踩了一连串编码坑——中文正文在后台编辑器里变成了一堆 \u9c \u80 \u91 的单字节乱码。这篇复盘既是原文内容的速览,也是这次"发布自动化"踩坑的完整记录,希望能帮到同样在搞博客自动发布的人。

一、原博客内容速览:《Hermes Agent 安装踩坑手记》

原文记录了在 Intel MacBook Pro(i7, macOS 13.7.8)+ 受限网络(github.com 超时)下安装 Hermes Agent v0.20.1 的 5 个关键坑与救命方案:

  1. 官方安装器 git fetch 死锁 —— install.sh 卡在 "Existing installation found, updating...",根因是 github.com:443 被屏蔽。方案:跳过安装器,本地仓库齐全就直接手动建 venv。
  2. --extra all 249 包源码编译卡死 —— [all] extra 拉入 400+ 重型依赖,任一缺 wheel 就触发 sdist 编译 + git+https 死锁。方案:最小化起步,只装核心依赖 uv pip install -e .。
  3. cryptography 50.0.0 放弃 Intel Mac —— PyPI 只有 arm64 wheel,锁版本又必须装。方案:装 Rust toolchain 从源码编译。
  4. openssl-sys 找不到 OpenSSL —— 连 openssl.org 下载源也 301 跳 github 被拦。方案:patch Cargo.toml 启用 vendored feature,openssl-src 自带源码从 crates.io 下载。
  5. uv 每次构建重新解包 sdist 覆盖 patch —— uv 保证可重现的独立解包策略反而吃掉了 patch。方案:把 patched 源码复制到稳定目录,用 uv pip install <本地路径>。

核心经验:当官方安装器不工作时,排查顺序是:卡在哪步 → 是否网络屏障 → 缺 wheel 还是缺工具链 → 编译依赖 → patch 是否真被吃到。

二、发布这篇手记时踩的坑:后台中文乱码

把上面这篇手记发布到 Emlog Pro 博客后台时,遇到了第二层坑——正文中文全部变成乱码。

现象

通过浏览器自动化把 markdown 正文注入后台编辑器并保存后,前台文章显示成一堆形如 æè¿å¨ 的乱码。后台编辑器里看到的是一串 \u9c \u80 \u91 这样的单字节"字符"。

根因:Latin-1 误解(每个 UTF-8 字节当一个 Unicode 码点)

中文"最"的 UTF-8 编码是 3 个字节 E6 9C 80。注入时用了 atob() 解码 Base64,但 atob() 返回的是一个 Latin-1 字符串——它把每个字节直接当作一个 Unicode 码点(得到 U+00E6, U+009C, U+0080 三个字符),而不是还原成"最"这个汉字。

当这串"Latin-1 误解"的字符串被写进编辑器并保存,数据库里存的就是 3 个独立字符而非 1 个汉字。前台用 UTF-8 渲染时,这 3 个字符又各自被编码成 2 字节(C3 A6, C2 9C, C2 80),于是显示成 æÂ 一类的乱码。

第一次"修复"为什么没生效

我最初改用了 decodeURIComponent(escape(atob(b64))) 来正确还原 UTF-8——这步逻辑是对的。但事后复盘发现:内容并没有真正写进 Emlog 的底层 textarea。Emlog Pro 后台用的是普通 <textarea name="logcontent">(没有 TinyMCE/KindEditor 等富文本编辑器),而我之前注入的目标元素不对,等于"修对了解码、写错了地方",保存的还是乱码。

最终修复方案

  1. 定位真正的存储元素:探测确认 KE=false / TM=false / XH=false / contenteditable=0,编辑器就是 <textarea name="logcontent">。
  2. 绕过 https 混合内容限制:原本想用本地 HTTP 服务 + XHR 喂数据,但 https 后台页禁止 http XHR(即使是 127.0.0.1)。
  3. 在 Exec 沙箱内串联 Shell + browser_evaluate:在代码执行沙箱里用 Shell 读取本地 Base64 文件,拼成 evaluate 脚本,用 decodeURIComponent(escape(atob(b64))) 正确解码后直接设置 textarea.value,并派发 input/change 事件。
  4. 绕过 readonly 按钮保存:保存按钮 states 是 readonly,DOM 点击不触发提交,改用 JS .click() 成功提交。

字节级验证(确认不是显示层巧合)

为排除"前台显示正确但数据库其实是双重编码"的可能,做了两处字节级验证:

两处一致,证明数据库存储的就是正确 UTF-8。

三、经验教训

  1. 注入前先确认目标元素:富文本编辑器(TinyMCE/KindEditor)和普通 textarea 的注入方式完全不同。先探测 window.tinyMCE / KindEditor / contenteditable 再决定写哪里,别盲目写。
  2. atob 返回 Latin-1,中文必须二次解码:decodeURIComponent(escape(atob(b64))) 是 Base64→UTF-8 的标准姿势,直接用 atob 的结果会得到每字节一码点的乱码。
  3. https 页面禁止 http 资源:本地 HTTP 服务器喂不可行,改用沙箱内 Shell 读文件 + evaluate 注入,或把内容内联。
  4. readonly/禁用按钮用 JS .click():DOM click 工具受 disabled/readonly 限制,element.click() 能绕过。
  5. 验证要到字节级:"显示正常"不等于"存储正确",双重编码会骗过眼睛。用 curl + 字节分析检查原始字节,或对比 encodeURIComponent 的输出,才能确认数据库真存对了。
  6. Exec 沙箱的 throw 输出法:沙箱默认不返回表达式值,用 throw new Error(JSON.stringify(...)) 能把诊断数据带出来,方便调试浏览器自动化。

如果你也在用 AI 助手或脚本自动发布博客到 Emlog,希望这篇复盘能帮你少走一些编码弯路。

—— 2026.08.16 于 島上隨筆

本文由 Trae CN AI 编程助手协助撰写,采用 Solo Agent 自主智能体模式完成博客发布与乱码修复全流程。

文章导出
预览框
生成预览中...

emlog 博客自动化 UTF-8 乱码 浏览器自动化

收到12条评论
avatar
产品经理 1 个月前
折腾这么多只为发一篇文章,投入产出比太低,直接手发更快。
commentator
島主 1 个月前
@产品经理:手发一次快,批量发一百篇就慢了。自动化是为可复用,不是为单次省时。
avatar
运维老兵 1 个月前
CodeMirror 同步问题说明没读 editor 文档就硬调,调研不充分。
commentator
島主 1 个月前
@运维老兵:承认调研不足,但 editor 的 save 方法在不同版本行为不一,文档也有歧义,实测才靠谱。
avatar
架构师 1 个月前
浏览器自动化发博客本质是人肉模拟,正确做法是接 API,这篇文章绕了大弯。
commentator
島主 1 个月前
@架构师:这正是文章的结论,乱码血泪史最后引出 blog-mcp 这条正路。绕弯的价值是记录所有坑,后人能避。
avatar
安全客 1 个月前
Base64 传中文绕过乱码,但等于把明文存进页面源码,等于公开内容。
commentator
島主 1 个月前
@安全客:内容本就是公开发布的文章,源码可见无额外风险。Base64 解决的是传输层编码,不是加密,别混淆。
avatar
前端小张 1 个月前
Latin1 误解 UTF8 这种基础编码问题,根因是没设 charset,治本应改后端头不是补解码。
commentator
島主 1 个月前
@前端小张:后端头改不了才走客户端补解码,这是受限场景的权衡。治本要改 emlog 源码,但升级会冲掉,补解码反而更稳。
avatar
山风 1 个月前
终于看到有人把 UTF-8 陷阱讲明白了。
avatar
山风 1 个月前
浏览器自动化 + emlog,这个组合很有参考价值。