利用博客园的MetaWeblog协议+nodejs同步自建博客中的md文件

背景

因为一直在使用 hexo 自建博客,最近又切换到了 docusaurus ,但是又想同时发布到博客园,所以需要一个工具能将 md 文件直接发布到博客园,所以写了一个 node 自动化上传脚本 ,同时方便需要的人借鉴使用(2023年1月更新)
下面简单描述下 MetaWeblog 协议的使用

博客园的 MetaWeblog 协议的使用

原资料地址

背景资料地址:https://www.cnblogs.com/caipeiyu/p/5354341.html

想实现自己的文章一处编写,多处发布到各大平台(比如博客园,CSDN)等要怎么实现呢。需要由这些组成:

  1. 文章管理:一个管理文章知识的平台(网站),在这里撰写,编辑文章。比如:写博客的客户端软件,博客园等。
  2. 第三方网站(平台)具有开放的 API 接口,比如博客园的 metaWebBlog。
  3. 同步服务:读取文章,调开放的 API,将文章发布出去。

一般来说,写文章的软件很容易获得,如果目标平台再有开放接口,我们可以将文章通过接口进行发布。

博客园支持 metaWebBlog 接口,使得可以接收来自 接口 的文章

1. metaWebBlog 概述

MetaWeblog API(MWA)是一个 Blog 程序接口标准。通过 MetaWeblog API,博客平台可以对外公布 blog 提供的服务,从而允许外面的程序新建,编辑,删除,发布 bolg。

MetaWeblog 使用 xml-RPC 作为通讯协议。

XML-RPC 是一个远程过程调用(远端程序呼叫)(remote procedure call,RPC)的分布式计算协议,通过 XML 将调用函数封装,并使用 HTTP 协议作为传送机制。一个 XML-RPC 消息就是一个请求体为 xml 的 http-post 请求,被调用的方法在服务器端执行并将执行结果以 xml 格式编码后返回。

简单理解就是:在 HTTP 请求 中,发送 xml 格式描述的“调用指令”,如果调用成功,会收到 xml 格式描述的“执行结果”。

2. 博客园文章相关接口

  • blogger.getUsersBlogs —— 获取用户博客信息
  • metaWeblog.getRecentPosts —— 获取最近的文章
  • metaWeblog.getPost —— 获取文章内容
  • metaWeblog.newPost —— 添加文章
  • metaWeblog.editPost —— 编辑文章
  • blogger.deletePost —— 删除文章

还有一些关于 文章分类 的接口,可以在其接口文档中找到。

2.1 接口说明

在 博客园 设置页面的地步可以找到 API 接口的说明,类似这样:

https://rpc.cnblogs.com/metaweblog/{userName}

上面的 {userName} 替换成实际的用户名。

下文仅说明“请求的接口和参数”,响应内容在发送成功后一看便知。

2.2 发送方式

使用 nodejs 完成文件读写和接口调用

资料

index.js


/*
 * @Description: 批量将hexo中的md文件上传博客园
 * @Autor: Bowen
 * @Date: 2021-10-09 16:56:43
 * @LastEditors: Bowen
 * @LastEditTime: 2023-01-11 10:09:47
 */

const fs = require("fs").promises;
const path = require("path");
const crypto = require("crypto");
const matter = require("gray-matter");
const { newPost, editPost } = require("./api");

// 发布所有的文章
async function handleAllPushPost(dirPath) {
  let files = await fs.readdir(dirPath);
  for (const fileName of files) {
    const filePath = path.resolve(dirPath, fileName);
    // dir 继续递归
    let stats = await fs.stat(filePath);
    if (stats.isDirectory()) {
      await handleAllPushPost(filePath);
      continue;
    }
    console.log("[********************************]");
    console.log("[fileName]", fileName);
    // await new Promise((r) => setTimeout(r, 1000), true);
    await handlePushPost(filePath);
  }
}

// 根据path修改或者新建文章
async function handlePushPost(filePath) {
  const fileName = path.basename(filePath);
  // 解析 md 文件
  const grayMatterFile = matter.read(filePath);
  const { data, content } = grayMatterFile;
  if (!data || !data.title) return;
  // 获取当前哈希值 对比 之前的 哈希
  const hash = crypto.createHash("sha256");
  hash.update(content);
  let nowContentHash = hash.digest("hex");
  let { cnblogs, hash: contentHash } = data;
  if (contentHash && contentHash == nowContentHash) {
    console.log("[hash值未变退出当前循环]");
    return;
  }
  // yaml中添加 hash
  data.hash = nowContentHash;
  // 文章数据
  const categories = Array.isArray(data.tags) ? data.tags : [];
  // TODO: data.? 看自己的 md 文档是如何配置
  const post = {
    description: content,
    title: data.title,
    // 注意 要以 Markdown 格式发布 必须在 categories 中 添加  "[Markdown]"
    categories: ["[Markdown]"].concat(categories),
  };
  let res;
  // 编辑
  if (cnblogs && cnblogs.postid) {
    console.log("[-------------修改-------------]");
    try {
      res = await editPost(cnblogs.postid, post, true);
    } catch (error) {
      console.log("[修改失败]", error.message);
      throw Error(error.message);
    }
    console.log("[修改成功]", res);
  } else {
    console.log("[-------------新建-------------]");
    data.cnblogs = {};
    try {
      res = await newPost(post, true);
    } catch (error) {
      console.log("[上传失败]", error.message);
      throw Error(error.message);
    }
    console.log("[上传成功]", res);
    // yaml中添加 postid
    data.cnblogs.postid = res;
  }
  // 回写数据
  const str = grayMatterFile.stringify();
  await fs.writeFile(filePath, str);
  console.log("[回写成功]", fileName);
  // 等待 1分钟 后继续下一个
  await new Promise((r) => setTimeout(r, 3500, true));
}

(async () => {
  await handleAllPushPost("C:/bowen/product/new-blog/docs");
  // await handleAllPushPost("C:/bowen/product/new-blog/blog");
})();


api.js

const MetaWeblog = require("metaweblog-api");
const apiUrl = "https://rpc.cnblogs.com/metaweblog/username"; // use your blog API instead
const metaWeblog = new MetaWeblog(apiUrl);

const username ="username"
const password ="password || token"
const blogid ='blogid' // 通过 getUsersBlogs 查询
const appKey =''
const numberOfPosts =1

module.exports = {
  getUsersBlogs:()=> metaWeblog.getUsersBlogs(appKey, username, password),
  getRecentPosts:()=> metaWeblog.getRecentPosts(blogid, username, password, numberOfPosts),
  getCategories:()=> metaWeblog.getCategories(blogid, username, password),
  getPost:(postid)=> metaWeblog.getPost(postid, username, password),
  newPost: (post, publish)=> metaWeblog.newPost(blogid, username, password, post, publish),
  editPost: ( postid ,post, publish)=> metaWeblog.editPost(postid, username, password, post, publish),
  deletePost: ()=> metaWeblog.deletePost(appKey, postid, username, password, publish),
}
posted @ 2021-10-11 14:58  __Bowen  阅读(214)  评论(1编辑  收藏  举报