用 Markdown + XML-RPC 批量发布 WordPress 文章:一套可复用的自动化流程

如果你习惯用 Markdown 写稿,却每次都要登录后台手动粘贴、调格式、改标题,那这套流程值得抄一遍。整条链路只用 Python 标准库,不装第三方包。

一、为什么选 XML-RPC 而不是 REST API

WordPress 同时提供 REST API 和 XML-RPC 两套写入接口。选 XML-RPC 的理由很实际:

  • 兼容性更广。 大量老站点、部分安全插件会限制 REST 写接口,但 XML-RPC 默认是开的。
  • 零依赖。 Python 自带 `xmlrpc.client`,不需要 requests、不需要处理鉴权 header。
  • 方法齐全。 `wp.newPost`、`wp.getPost`、`wp.getPosts`、`wp.deletePost` 覆盖了发布、读回、列表、删除的全流程。

二、第一步:确认站点真的开了 XML-RPC

很多人卡在这里。正确判定方法不是看后台开关,而是直接发一个 GET 请求:

“`bash

curl -sS -o /dev/null -w “%{http_code}\n” https://你的域名/xmlrpc.php

“`

  • 返回 405,且响应体是 `XML-RPC server accepts POST requests only.` → 说明已开启,可以继续。
  • 返回 404 / 403,或返回一整页 HTML → 说明被插件、CDN 或防火墙拦截了。

还有一个高频坑:地址必须写 https。如果站点套了 Cloudflare,你用 `http://` 发起请求会收到 301 跳转到 https,而 XML-RPC 客户端不会自动跟随跳转,于是直接报错。这个错误信息通常很难懂,实际原因只是少写了一个 s。

三、第二步:用应用密码,不要用登录密码

WordPress 后台「用户 → 个人资料 → 应用程序密码」可以生成一串专用口令。相比登录密码,它有三个好处:可单独吊销、不影响主账号登录、可以被安全插件豁免强制 2FA。

脚本里不要写死口令,用环境变量传入:

“`bash

export WP_XMLRPC_URL=”https://你的域名/xmlrpc.php”

export WP_USERNAME=”你的用户名”

export WP_APP_PASSWORD=”生成的应用程序密码”

“`

这样脚本本身可以放心提交到 Git。

四、第三步:标题提取与去重

Markdown 稿件的第一个一级标题,天然适合当作 WordPress 的文章标题。取出来之后要从正文里删掉,否则页面会出现重复标题:

“`python

def extract_title(markdown_text):

lines = markdown_text.split(“\n”)

title, body = None, []

for line in lines:

if title is None and line.lstrip().startswith(“# “):

title = line.lstrip()[2:].strip()

continue

body.append(line)

return title, “\n”.join(body).lstrip(“\n”)

“`

去重则不需要自己建表——直接问 WordPress 要现有的标题列表,比一遍就行:

“`python

posts = server.wp.getPosts(

1, user, password,

{“number”: 200, “post_status”: “any”, “post_type”: “post”},

[“post_id”, “post_title”],

)

existing = {p[“post_title”].strip() for p in posts}

“`

注意 `post_status` 传 `any`,这样草稿和已发布的都会纳入比对,避免同一天跑两次产生重复稿。

五、第四步:Markdown 转 HTML 够用就行

不需要上完整的 Markdown 解析器。博客正文常用的就那几种:标题、段落、图片、加粗、有序/无序列表、分隔线。写一个百来行的转换器足矣,关键是有序列表和无序列表要能正确闭合——很多简陋实现会在列表切换处漏掉 `

`,导致整页排版塌掉。

六、第五步:默认发草稿,这一步能救命

自动化最大的风险不是发布失败,而是发布成功了但内容不该发。所以状态默认值应该是 `draft`,必须显式传参才公开:

“`python

post = {

“post_title”: title,

“post_content”: html,

“post_status”: “draft”, # 默认草稿

“post_type”: “post”,

}

post_id = server.wp.newPost(1, user, password, post)

“`

草稿发完,用 `wp.getPost` 读回来核对标题、状态和正文 HTML,确认无误后再去后台点发布。

七、常见报错对照

| 现象 | 真实原因 | 处理 |

| — | — | — |

| 连接超时 / 301 | 地址写成了 http | 改成 https |

| `Fault 403` 认证失败 | 用了登录密码或账号无发布权限 | 换应用密码,确认角色权限 |

| 返回一整页 HTML | XML-RPC 被插件或 CDN 拦截 | 在站点侧放开 `/xmlrpc.php` |

| 正文列表错乱 | 转换器未闭合列表标签 | 检查 `

` / `

` |

| 出现重复文章 | 去重时未包含草稿 | `post_status` 改为 `any` |

八、小结

整套流程的核心其实只有三条:https 地址、应用密码、默认草稿。剩下都是格式转换的体力活。把这三条守住,批量发布就可以放心交给脚本。


本文为技术流程说明,文中代码片段已在 Python 3.12 + WordPress XML-RPC 环境验证。