🚀

用 Python 写一个微信公众号定时发布助手,告别熬夜排版

读取本地 Markdown 文件,自动转为富文本并发布到公众号后台。配合 crontab 实现定时推送,打工人也能准时下班。

👁️ - 次阅读

凌晨两点还在改公众号排版?明天要发的稿子临时出了 bug?

这种痛苦每个做自媒体的都经历过。

今天做一个微信公众号定时发布助手。你只管写 Markdown,脚本负责排版、上传封面、设置定时推送。写完保存,到点自动发出去。

项目背景

微信公众号后台的编辑器不好用。Markdown 转富文本是个刚需。

更麻烦的是发布时间。你想在早上八点半发,但那时候你可能在睡觉。

手动操作可以,但容易忘。用脚本把流程自动化,设定好时间就能准时推送。

这个工具适合:

  • 个人公众号运营者,想批量管理多篇草稿
  • 团队内容编辑,统一排版风格
  • 需要固定时间推送的自媒体账号

技术选型

组件选择理由
API 调用requests简单直接,够用
Markdown 渲染markdown + html5lib转富文本基础
素材上传微信素材接口封面图自动上传
定时调度APScheduler / crontab灵活可控
配置管理.env 文件安全存储凭证

注意:微信开放平台需要先在 mp.weixin.qq.com 注册应用,获取 appid 和 secret。个人订阅号也能调用素材管理和草稿箱接口。

实现步骤

第一步:获取微信 access_token

# wechat_token.py
import requests
import json
import os
from dotenv import load_dotenv

load_dotenv()

APP_ID = os.getenv("WECHAT_APP_ID")
APP_SECRET = os.getenv("WECHAT_APP_SECRET")

def get_access_token():
    """获取微信 access_token,有效期2小时"""
    url = f"https://api.weixin.qq.com/cgi-bin/token"
    params = {
        "grant_type": "client_credential",
        "appid": APP_ID,
        "secret": APP_SECRET
    }
    resp = requests.get(url, params=params)
    data = resp.json()
    
    if "access_token" in data:
        print(f"✅ Token 获取成功,有效期 {data['expires_in']} 秒")
        return data["access_token"]
    else:
        raise Exception(f"Token 获取失败: {data}")

第二步:上传封面图片

# wechat_media.py
import requests
from wechat_token import get_access_token

def upload_cover(image_path):
    """上传封面图到微信服务器,返回 media_id"""
    token = get_access_token()
    url = f"https://api.weixin.qq.com/cgi-bin/material/add_material"
    
    params = {
        "access_token": token,
        "type": "image"
    }
    
    with open(image_path, "rb") as f:
        files = {"media": (os.path.basename(image_path), f, "image/jpeg")}
        resp = requests.post(url, params=params, files=files)
    
    result = resp.json()
    if "media_id" in result:
        print(f"✅ 封面图上传成功,media_id: {result['media_id']}")
        return result["media_id"]
    else:
        raise Exception(f"封面上传失败: {result}")

第三步:Markdown 转富文本

# wechat_formatter.py
import markdown
import re
import os

def md_to_wechat_html(md_content):
    """将 Markdown 转为微信公众号可用的富文本 HTML"""
    # 扩展语法支持
    extensions = ["tables", "fenced_code", "codehilite", "toc"]
    
    # 转换 Markdown 为 HTML
    html_body = markdown.markdown(md_content, extensions=extensions)
    
    # 微信不支持某些标签,做替换
    # 代码块添加样式
    html_body = re.sub(
        r'<pre><code class="language-(\w+)">(.*?)</code></pre>',
        r'<div style="background:#f5f5f5;padding:12px;border-radius:6px;font-family:monospace;">\2</div>',
        html_body,
        flags=re.DOTALL | re.IGNORECASE
    )
    
    # 段落添加间距
    html_body = re.sub(r'</p>\s*<p>', '</p><p>', html_body)
    
    # 标题加粗着色
    html_body = re.sub(
        r'<h(\d)>(.*?)</h\1>',
        lambda m: f'<strong style="font-size:{18-int(m.group(1))*2}px;color:#333;">{m.group(2)}</strong>',
        html_body
    )
    
    return html_body

def render_article(title, subtitle, content_md, cover_media_id):
    """组装完整的文章数据"""
    new_article_html = md_to_wechat_html(content_md)
    
    article_data = {
        "title": title,
        "author": "作者名",
        "digest": subtitle,
        "content": new_article_html,
        "thumb_media_id": cover_media_id,
        "need_open_comment": 1,
        "only_fans_can_comment": 0
    }
    return article_data

第四步:创建草稿并定时发布

# wechat_draft.py
import requests
import json
from datetime import datetime, timedelta
from wechat_token import get_access_token
from wechat_formatter import render_article

def create_draft(article_data):
    """创建公众号草稿"""
    token = get_access_token()
    url = f"https://api.weixin.qq.com/cgi-bin/draft/batchsave"
    
    params = {"access_token": token}
    payload = {"articles": [article_data]}
    
    resp = requests.post(url, params=params, json=payload)
    result = resp.json()
    
    if "media_id" in result:
        print(f"✅ 草稿创建成功,media_id: {result['media_id']}")
        return result["media_id"]
    else:
        raise Exception(f"草稿创建失败: {result}")

def schedule_publish(media_id, publish_time_str=None):
    """
    定时发布草稿
    publish_time_str: ISO 格式时间字符串,如 "2026-07-06T08:30:00+08:00"
    如果不传,默认立即发布
    """
    token = get_access_token()
    
    if publish_time_str:
        # 定时发布
        url = f"https://api.weixin.qq.com/cgi-bin/freepublish/submit"
        payload = {
            "media_id": media_id,
            "freepublish_status": 2,  # 2=定时发布
            "publish_time": int(datetime.fromisoformat(publish_time_str).timestamp())
        }
    else:
        # 立即发布
        url = f"https://api.weixin.qq.com/cgi-bin/freepublish/submit"
        payload = {
            "media_id": media_id,
            "freepublish_status": 0   # 0=立即发布
        }
    
    params = {"access_token": token}
    resp = requests.post(url, params=params, json=payload)
    result = resp.json()
    
    if result.get("errcode") == 0:
        print("✅ 发布任务提交成功!")
    else:
        print(f"⚠️ 发布结果: {result}")
    
    return result

# ========== 主入口 ==========
if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="微信公众号定时发布助手")
    parser.add_argument("--title", required=True, help="文章标题")
    parser.add_argument("--subtitle", default="", help="摘要")
    parser.add_argument("--md-file", required=True, help="Markdown 文件路径")
    parser.add_argument("--cover", required=True, help="封面图片路径")
    parser.add_argument("--schedule", default=None, help="定时发布时间 ISO 格式")
    args = parser.parse_args()
    
    # 读取 Markdown 内容
    with open(args.md_file, "r", encoding="utf-8") as f:
        content = f.read()
    
    # 上传封面
    cover_media_id = upload_cover(args.cover)
    
    # 渲染文章
    article = render_article(args.title, args.subtitle, content, cover_media_id)
    
    # 创建草稿
    draft_media_id = create_draft(article)
    
    # 发布
    schedule_publish(draft_media_id, args.schedule)

第五步:准备 Markdown 文章

创建一个示例文章 my_article.md

# Python 自动化发布教程

这是一篇测试文章。

## 为什么需要自动化?

手动发布太慢了。每次都要打开后台,复制粘贴,调整排版。

## 代码示例

```python
print("Hello, WeChat!")

小贴士:记得先获取 access_token 再调用接口。


感谢阅读,欢迎收藏转发。


### 第六步:运行

```bash
# 安装依赖
pip install requests markdown python-dotenv pygments

# 创建 .env 文件
echo "WECHAT_APP_ID=your_app_id" > .env
echo "WECHAT_APP_SECRET=your_app_secret" >> .env

# 立即发布
python wechat_draft.py \
  --title "我的第一篇自动化推文" \
  --subtitle "用 Python 自动发布公众号文章" \
  --md-file my_article.md \
  --cover cover.jpg

# 定时发布(明天早上八点)
python wechat_draft.py \
  --title "定时推送测试" \
  --md-file my_article.md \
  --cover cover.jpg \
  --schedule "2026-07-06T08:00:00+08:00"

运行效果

跑完脚本后,你会看到类似这样的输出:

✅ Token 获取成功,有效期 7200 秒
✅ 封面图上传成功,media_id: xxxxxxxxxxx
✅ 草稿创建成功,media_id: yyyyyyyyyyy
✅ 发布任务提交成功!

登录微信公众号后台,在"草稿箱"里能看到刚刚创建的草稿。如果是定时发布,到了指定时间会自动推送给订阅用户。

优化方向

  1. 批量发布:读取目录下所有 .md 文件,一次性创建多篇草稿
  2. 模板系统:用 Jinja2 做文章模板,统一标题样式、页头页脚
  3. 预览功能:调用预览接口,发到指定微信号先看看效果
  4. 自动获取 token:把 token 存到 Redis,避免频繁请求被限流
  5. CI/CD 集成:配合 GitHub Actions,push 代码后自动触发发布
  6. 多账号支持:配置文件里写多个 appid/secret,一键切换

有了这个工具,晚上写好文章,设定好时间,安心睡觉。第二天醒来,推文已经发出去了。

要不要试试把你手头的文章丢进来跑一圈?

这篇文章对你有帮助吗?