用 AI Agent 自动发布华为云博客:从踩坑到成功的完整实战

举报
华为云训练营小助手 发表于 2026/07/06 17:49:43 2026/07/06
【摘要】 本文记录了使用华为云码道(CodeArts)代码智能体,通过 Playwright 浏览器自动化 + API 逆向分析,实现博客自动发布的完整过程。包含 8 个关键问题的排查与修复,供开发者参考。 背景 华为云开发者成长中心提供了"发布文章"成长任务,完成可获得 +60 积分。但手动写博客、排版、发布流程繁琐。我们希望用 AI Agent 全自动完成:生成文章内容 → 打开编辑器 → 填写标题

本文记录了使用华为云码道(CodeArts)代码智能体,通过 Playwright 浏览器自动化 + API 逆向分析,实现博客自动发布的完整过程。包含 8 个关键问题的排查与修复,供开发者参考。

背景

华为云开发者成长中心提供了"发布文章"成长任务,完成可获得 +60 积分。但手动写博客、排版、发布流程繁琐。我们希望用 AI Agent 全自动完成:生成文章内容 → 打开编辑器 → 填写标题/正文/标签 → 点击发表。

听起来简单,实际踩坑无数。以下是完整实战记录。

整体流程

自动发布博客的流程分为以下步骤:

  1. 扫码登录:通过 Playwright 弹出浏览器,用户华为云 APP 扫码,Cookie 自动保存
  2. 打开写文章页面:访问博客编辑器 URL
  3. 填写标题:通过 Playwright fill API 输入标题
  4. 填写正文:通过 TinyMCE 编辑器 API 设置 HTML 内容
  5. 选择标签:通过 tagator 插件交互选择标签
  6. 点击发表:调用博客发布 API

踩坑记录

坑1:写文章页面 URL 找不到

问题:尝试了多个 URL 均返回 404:

  • /blogs/post → 404
  • /community/writeblog → 404
  • /blogs/write → 404

修复:通过搜索华为云社区页面,最终找到正确 URL:

https://bbs.huaweicloud.cn/blogs/article

坑2:TinyMCE 编辑器内容设置后不生效

问题:使用 Playwright 直接操作 iframe 内的 contenteditable 区域失败,因为 TinyMCE 编辑器有独立的事件系统。

修复:使用 TinyMCE 官方 API 设置内容,并触发事件通知编辑器内部状态更新:

tinymce.activeEditor.setContent(htmlContent);
tinymce.activeEditor.fire('change');
tinymce.activeEditor.fire('input');
tinymce.activeEditor.save();

坑3:标签选择器 tagator 插件选错标签

问题:通过 tagator 输入框输入搜索词"华为云码道"后,点击下拉选项,但实际选中的标签变成了"ABAP",而非目标标签。

原因:tagator 下拉选项的 DOM 元素可能包含 1000+ 个选项(全量标签),部分选项不可见但仍然存在于 DOM 中,导致点击了错误的元素。

修复:放弃通过 tagator UI 交互选择标签,改为直接设置 #blogLable input 的值,并重新初始化 tagator 插件:

const blogLabel = document.getElementById('blogLable');
blogLabel.value = '华为云码道(CodeArts)代码智能体';
blogLabel.dispatchEvent(new Event('change', { bubbles: true }));
$(blogLabel).tagator({ autocomplete: allInfoArray, useDimmer: true });

坑4:发表按钮始终禁用

问题:发表按钮 #submit-btn 始终带有 write-blog-publish-btn-no 禁用样式类,即使标题、内容、标签都已填写完毕。

排查过程

  • 检查按钮的 onclick 属性 → 无
  • 检查 jQuery 事件绑定 → $._data(btn, "events") 返回空
  • 尝试调用 window.saveBlog() → 报错 Cannot read properties of undefined (reading 'isLock')
  • 手动移除禁用样式类并点击 → 无效,按钮点击没有触发任何请求

根本原因:编辑器内部验证逻辑无法感知通过 API 设置的内容和标签,导致按钮永远处于禁用状态。

修复:绕过前端 UI,直接调用后端 API 发布博客(见坑5)。

坑5:博客发布 API 端点和字段名

问题:不知道博客发布的 API 端点和请求体格式。

排查过程

  1. 通过 saveBlog.toString() 获取函数源码,发现发布 API 端点为:
/rest/developer/fwdu/rest/developer/user/hdblogservice/v1/blog/blogging
  1. 搜索页面 script 标签,找到 submitData 的完整构造逻辑:
var blogData = {};
blogData.tagNameData = tagList.toString();
blogData.titleData = $("#blogTitle").val();
blogData.introData = blog_Summry;
blogData.contentData = editorContent.toString();
blogData.markdownContent = '...';
blogData.imageUrl = [];

关键发现:API 字段名不是 title/content/label,而是 titleData/contentData/tagNameData。第一次调用时使用了错误的字段名 label,虽然 API 返回成功,但博客页面显示"Render Error"。

坑6:博客页面显示"Render Error"

问题:使用 label 字段(而非 tagNameData)调用 API 后,API 返回成功,但打开博客页面显示"Render Error: "。

原因:API 调用缺少 introData(摘要)和 tagNameData(标签名)两个必填字段。

修复:补全所有必填字段后重新调用:

const submitData = {
  titleData: title,
  contentData: contentHtml,
  introData: plainText.substring(0, 200),  // 摘要
  tagNameData: tagLabel,                    // 标签名
  isLock: 0,
  blogResource: 1,                          // 原创
  isReship: 1,                              // 允许转载
  disclaimerFlag: 1,
  blogType: 0,
  isOriginal: 1,
  source: 1,
};

坑7:Markdown 转 HTML 格式错乱

问题:博客发布后,代码块显示为单反引号、列表没有格式化、表格显示为纯文本、标题未渲染。

原因:最初的 Markdown 转 HTML 使用简单的正则替换,无法正确处理代码块、列表、表格等复杂结构。

修复:重写 Markdown 转 HTML 解析器,逐行解析,支持完整的 Markdown 语法:

  • 代码块( `language )→
  • 无序列表(- item)→
    • 有序列表(1. item)→
      1. 表格(| a | b |)→
      2. 引用(> text)→
      3. 标题(## text)→

      4. 行内格式(bold、` code )→ `
      5. 坑8:Windows 换行符导致标题不渲染

        问题:重写 Markdown 解析器后,标题仍然没有被转换为

        标签,而是显示为

        ## 背景


        原因:Windows 环境下文件使用 \r\n 换行符,split('\n') 后每行末尾残留 \r,导致正则表达式 /^(#{1,6})\s+(.+)$/ 中的 $ 无法正确匹配行尾。

        修复:在解析前统一处理换行符:

        const lines = md.replace(/\r\n/g, '\n').replace(/\r/g, '').split('\n');

        最终方案

        完整的自动发布博客流程如下:

        1. 通过 Playwright 打开写文章页面,注入登录 Cookie
        2. 使用 Playwright fill API 填写标题
        3. 使用 TinyMCE API setContent 填写正文(Markdown 预先转为 HTML)
        4. 触发编辑器 blur 事件让系统自动提取标签,或手动设置标签值
        5. 获取 CSRF Token(通过 /api/get-ainfo 接口)
        6. 构造完整的 submitData,直接调用 /blog/blogging API 发布
        7. 解析返回的 blogId,验证发布结果

        关键代码片段

        Markdown 转 HTML 解析器核心逻辑

        function mdToHtml(md) {
          const lines = md.replace(/\r\n/g, '\n').replace(/\r/g, '').split('\n');
          let html = '';
          let i = 0;
          while (i < lines.length) {
            const line = lines[i].trimEnd();
            // 代码块
            if (line.startsWith('```')) { /* ... */ }
            // 标题
            const hMatch = line.match(/^(#{1,6})\s+(.+)$/);
            if (hMatch) {
              html += `<h${hMatch[1].length}>${inlineFormat(hMatch[2])}</h${hMatch[1].length}>`;
              i++; continue;
            }
            // 列表、表格、引用、段落...
          }
          return html;
        }

        直接调用发布 API

        const resp = await fetch(
          devdataDomain + '/rest/developer/fwdu/rest/developer/user/hdblogservice/v1/blog/blogging',
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'csrf': csrfToken },
            body: JSON.stringify(submitData),
            credentials: 'include',
          }
        );

        经验总结

        1. 前端 UI 自动化有上限:当 UI 组件内部状态与 DOM 不同步时(如 TinyMCE、tagator),直接操作 DOM 往往无效。此时应转向 API 层面解决问题。

        1. 逆向分析 JS 源码是关键:通过 function.toString() 获取函数源码,通过搜索

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。