用 Python + Streamlit 做一个微信公众号文章自动排版工具

你是不是每次写完公众号文章,还要手动调字号、改颜色、对齐间距?排版花的时间比写内容还多。

今天做一个能自动帮你排版的工具。粘贴 Markdown,选好主题色,直接导出微信可用的 HTML。

项目背景

公众号排版有个痛点:微信编辑器不支持 Markdown,也不支持外部 CSS。所有样式必须内联到 HTML 标签里。

很多人用第三方排版工具,但要么收费,要么有水印,要么数据不安全。自己做一个,本地跑,样式全由你控制。

技术选型

|| 组件 | 选型 | 理由 ||——|——|——|| 语言 | Python 3.10+ | 脚本开发快 || 渲染引擎 | markdown-it-py | 轻量级 Markdown 转 HTML || 前端框架 | Streamlit | 不用写 HTML/CSS 就能做交互界面 || 样式输出 | 自定义 CSS 注入 | 导出微信兼容的内联样式 |

不需要数据库,不需要登录系统。一个 Streamlit 应用搞定。

实现步骤

第一步:安装依赖

pip install streamlit markdown-it-py pygments

就三个包。streamlit 做界面,markdown-it-py 解析 Markdown,pygments 做代码高亮。

第二步:创建排版引擎 formatter.py

from markdown_it import MarkdownIt
from markdown_it_pygments import pygments_renderer
from pygments.formatters import HtmlFormatter

class WeChatFormatter:
    """微信公众号文章排版引擎"""
    
    # 预设主题配色
    THEMES = {
        "default": {"bg": "#fff", "text": "#333", "link": "#576b95", "accent": "#576b95"},
        "warm": {"bg": "#faf8f5", "text": "#4a3f35", "link": "#c45e43", "accent": "#c45e43"},
        "cool": {"bg": "#f5f7fa", "text": "#2c3e50", "link": "#3498db", "accent": "#3498db"},
        "dark": {"bg": "#1a1a2e", "text": "#e0e0e0", "link": "#e94560", "accent": "#e94560"},
    }
    
    def __init__(self, theme="default"):
        self.theme = self.THEMES.get(theme, self.THEMES["default"])
        self.md = MarkdownIt("commonmark", {
            "html": True,
            "highlight": None,
        })
        
    def render(self, markdown_text):
        """将 Markdown 渲染为微信兼容的 HTML"""
        html = self.md.render(markdown_text)
        html = self._inject_styles(html)
        return html
    
    def _inject_styles(self, html):
        """注入微信兼容的内联样式"""
        t = self.theme
        
        # 基础容器样式
        container_style = (
            f'style="background:{t["bg"]};color:{t["text"]};'
            f'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",'
            f'"PingFang SC","Hiragino Sans GB","Microsoft YaHei",'
            f'sans-serif;font-size:16px;line-height:1.8;padding:20px;"'
        )
        
        # 段落样式
        html = html.replace('<p>', f'<p style="margin:1em 0;">')
        
        # 标题样式
        for level in range(1, 7):
            tag = f'h{level}'
            sizes = {1: '22px', 2: '20px', 3: '18px', 4: '16px', 5: '14px', 6: '14px'}
            weights = {1: 'bold', 2: 'bold', 3: 'bold', 4: 'normal', 5: 'normal', 6: 'normal'}
            size = sizes[level]
            weight = weights[level]
            padding = '0.5em 0' if level <= 2 else '0.3em 0'
            
            old = f'<{tag}>'
            new = (f'<{tag} style="font-size:{size};font-weight:{weight};'
                   f'color:{t["accent"]};padding:{padding};'
                   f'border-bottom:2px solid {t["accent"]};margin:1.2em 0 0.6em;">')
            html = html.replace(old, new, 1) if level == 1 else html.replace(f'<{tag}>', new)
        
        # 链接样式
        html = html.replace('<a ', f'<a style="color:{t["link"]};text-decoration:none;border-bottom:1px solid {t["link"]}" ')
        
        # 引用块样式
        html = html.replace('<blockquote>', 
            f'<blockquote style="border-left:4px solid {t["accent"]};'
            f'background:{t["bg"]};padding:12px 16px;margin:1em 0;'
            f'color:{t["text"]};opacity:0.9;">')
        
        # 代码块样式
        code_style = (f'style="background:#f4f4f5;padding:12px;border-radius:6px;'
                      f'font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;'
                      f'font-size:13px;color:#24292e;overflow-x:auto;white-space:pre-wrap;"')
        html = html.replace('<code>', f'<code {code_style}>')
        html = html.replace('<pre>', f'<pre style="margin:1em 0;">')
        
        # 图片样式
        html = html.replace('<img ', '<img style="max-width:100%;border-radius:8px;display:block;margin:1em auto;" ')
        
        # 列表样式
        html = html.replace('<li>', '<li style="margin:0.4em 0;">')
        
        # 分割线
        html = html.replace('<hr>', f'<hr style="border:none;height:1px;background:{t["accent"]};opacity:0.3;margin:2em 0;">')
        
        # 包裹容器
        html = f'<div {container_style}>{html}</div>'
        
        return html
    
    def get_css_snippet(self):
        """返回可复制的 CSS(用于微信编辑器自定义样式区域)"""
        t = self.theme
        return f'''/* 微信排版主题: {self.name} */
::root {{
  --bg: {t["bg"]};
  --text: {t["text"]};
  --accent: {t["accent"]};
  --link: {t["link"]};
}}
body {{ background: var(--bg); color: var(--text); }}
h1,h2,h3 {{ color: var(--accent); }}
a {{ color: var(--link); }}'''

核心思路:用 markdown-it-py 把 Markdown 转成 HTML,然后在关键标签上注入内联 style。微信编辑器只认内联样式,不认外部 CSS。

第三步:创建 Streamlit 界面 app.py

import streamlit as st
from formatter import WeChatFormatter
import time

st.set_page_config(page_title="微信文章排版器", layout="wide")

st.title("📝 微信公众号文章自动排版工具")
st.markdown("粘贴 Markdown 内容,选择主题,一键导出微信兼容的 HTML。")

# 侧边栏设置
st.sidebar.header("⚙️ 设置")
theme = st.sidebar.selectbox("主题", list(WeChatFormatter.THEMES.keys()))
font_size = st.sidebar.slider("正文字号", 12, 20, 16)
show_preview = st.sidebar.checkbox("实时预览", value=True)

# 输入区域
tab_input, tab_demo = st.tabs(["📥 输入 Markdown", "📋 示例"])

with tab_demo:
    st.markdown("""
**试试这段示例内容:**

```markdown
# 你好,世界

这是一段**粗体**文字和一段*斜体*文字。

## 功能特点

- 自动适配微信样式
- 支持深色模式
- 一键导出 HTML

> 引用块会自动加上左侧边框

[点击这里](https://example.com) 访问链接。

### 代码示例

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

“”")

with tab_input: markdown_input = st.text_area( “粘贴你的 Markdown 内容”, height=400, placeholder="# 在这里粘贴你的文章内容…\n\n支持所有标准 Markdown 语法。", )

格式化按钮

if markdown_input.strip(): formatter = WeChatFormatter(theme)

if st.button("🚀 生成排版", type="primary"):
    with st.spinner("正在排版中..."):
        time.sleep(0.5)
        html_output = formatter.render(markdown_input)
    
    st.success("✅ 排版完成!")
    
    col1, col2 = st.columns([2, 1])
    
    with col1:
        st.subheader("👁️ 预览")
        st.components.v1.html(html_output, height=600, scrolling=True)
    
    with col2:
        st.subheader("📦 导出")
        st.download_button(
            label="下载 HTML 文件",
            data=html_output,
            file_name="article.html",
            mime="text/html",
        )
        
        st.markdown("**复制 HTML 源码**")
        st.code(html_output, language="html")
        
        st.markdown("**使用说明:**")
        st.info("""
  1. 点击下方「复制 HTML 源码」按钮
  2. 打开微信公众平台编辑器
  3. 切换到 HTML 模式(或粘贴到第三方编辑器)
  4. 预览效果,满意后发布 “”") else: st.info(“在左侧粘贴 Markdown 内容,点击「生成排版」开始。”)

界面很简单:左边写 Markdown,右边预览效果,底部导出 HTML。Streamlit 的好处是你不用碰一行前端代码。

### 第四步:运行应用

```bash
streamlit run app.py

浏览器会自动打开 http://localhost:8501。

输入框里粘贴任意 Markdown,选一个主题色,点生成。右边实时显示排版效果,底部可以下载 HTML 文件或复制源码。

运行效果

跑起来之后,操作流程是这样的:

  1. 写一篇文章,存为 .md 文件或用编辑器写好
  2. 复制到输入框,选主题色(推荐 “warm” 暖色调,适合大多数公众号)
  3. 点生成,右边预览效果
  4. 满意后下载 HTML,或者复制源码到微信编辑器
  5. 发布

整个流程从粘贴到导出,大概 10 秒。

生成的 HTML 可以直接粘贴到以下工具中使用:

  • 微信公众号后台的「源代码」模式
  • 135 编辑器、秀米等第三方排版工具的 HTML 导入功能
  • 直接作为网页发布

优化方向

这个版本已经能用了,想继续打磨也可以:

  1. 更多主题:加入节日主题、品牌主题,让用户自定义配色方案
  2. 封面图插入:支持在文章顶部自动插入封面图,尺寸自适应
  3. 摘要提取:从 Markdown 中自动提取前 120 字作为公众号摘要
  4. 字数统计:实时显示字数、段落数、图片数
  5. 模板库:预设新闻类、教程类、故事类等常用排版模板
  6. 批量处理:一次导入多个 Markdown 文件,统一风格批量导出
  7. AI 润色:接入大模型 API,对文章内容做简要润色建议

核心排版逻辑就在 formatter.py 那不到 100 行代码里。先跑通,再按需加功能。

打开终端,streamlit run app.py,你的第一个微信排版工具就诞生了。要不要试试把你的草稿丢进去看看效果?