用 Python 脚本自动发布 WordPress 文章:一套可落地的工作流程

在做内容站或者 AI 内容分发项目时,如何高效地把生成好的内容批量发布到 WordPress? 本文将分享我如何用一段 Python 脚本构建出一套自动化内容发布流程,不依赖 WordPress 后台操作、不需要 XML 导入,直接对接数据库,效率拉满。

💡 场景背景

假设你有一个独立的内容生成平台(或爬虫系统),将内容写入某个 MySQL 数据库。你希望每天自动将符合条件的文章发布到 WordPress 博客站点,减少人工操作。

这时候,一段 Python 脚本就能帮你完成:

  • 拉取来源数据库中某天的文章数据;
  • 自动创建 WordPress 用户(如果文章作者在 WordPress 中尚未存在);
  • 把文章写入 WordPress 的 wp_posts 表,设为草稿;
  • 设置特色图片(非上传,仅保存 URL);
  • 将文章分类绑定到 WordPress 的某个 term;
  • 自动设定发布时间(可用于后续定时发布)。

🧰 整体流程图

[内容数据库] → [Python 脚本处理] → [WordPress 数据库]
       ↑                             ↓
   获取文章                  写入 wp_posts、wp_postmeta、wp_term_relationships
                          自动创建用户(wp_users)

🛠️ 脚本的工作流程拆解

1. 脚本启动时传入日期参数(可选)

通过命令行执行:

python post_ai.py       # 默认取今天的文章
python post_ai.py 1     # 取昨天的文章

内部会根据当前时间减去 day_offset,获取对应日期的内容。

2. 连接来源数据库,获取指定日期的文章数据

SELECT * FROM articles_data
WHERE data_type_id = 49
AND create_time LIKE '2025-04-20%'

3. 遍历每一篇文章,开始处理发布逻辑

✅ 获取或创建 WordPress 用户

cursor.execute(f"SELECT ID FROM wp_users WHERE user_login = '{user_name}'")

如果不存在,则写入 wp_users 表。

✅ 构造 WordPress post 内容

提取字段包括:

  • 标题(title / main_title
  • 摘要(description / subtitle
  • 正文(content
  • 封面图 URL(image_url

插入 wp_posts

cursor.execute("""
INSERT INTO wp_posts (
    post_author, post_date, post_date_gmt, post_content, post_title,
    post_excerpt, post_status, comment_status, ping_status, post_name,
    post_modified, post_modified_gmt, post_type
) VALUES (...)
""")

✅ 设置特色图像

cursor.execute("""
INSERT INTO wp_postmeta (post_id, meta_key, meta_value)
VALUES (%s, 'featured_image', %s)
""")

✅ 设置分类

category_map = {
    '263': 9,
    '264': 10,
    '265': 8,
    '266': 6
}

插入 wp_term_relationships 表:

cursor.execute("""
INSERT INTO wp_term_relationships (object_id, term_taxonomy_id)
VALUES (%s, %s)
""")

📋 示例:一天 8 篇文章的调度逻辑

如果当日有 8 篇文章,脚本会均匀排布发布时间:

序号 发布时间
1 08:00
2 11:00
3 14:00

⚙️ 使用方式(命令行)

python post_ai.py       # 发布今天的文章
python post_ai.py 1     # 发布昨天的文章
python post_ai.py 2     # 发布前天的文章

🔒 安全提示 & 拓展建议

  • 建议先在测试站点运行此脚本,避免破坏正式环境数据;
  • 可将封面图 URL 上传为附件并生成 wp_attachment 类型;
  • 若有 Tag 需求,可拓展写入 wp_termswp_term_taxonomy
  • 建议结合 crontab 定时运行,或接入 Airflow 实现发布链路。

📄 完整脚本

# post_ai.py

import pymysql
import time
import datetime
import sys

db = pymysql.connect(host='localhost', user='root', password='password', database='source_db', charset='utf8mb4')
wp_db = pymysql.connect(host='localhost', user='wp_user', password='wp_pass', database='wordpress_db', charset='utf8mb4')
cursor = db.cursor()
wp_cursor = wp_db.cursor()

day_offset = int(sys.argv[1]) if len(sys.argv) > 1 else 0
target_date = (datetime.datetime.now() - datetime.timedelta(days=day_offset)).strftime('%Y-%m-%d')

sql = f"SELECT * FROM articles_data WHERE data_type_id = 49 AND create_time LIKE '{target_date}%'"
cursor.execute(sql)
results = cursor.fetchall()

interval = 180
start_time = datetime.datetime.now().replace(hour=8, minute=0)

print(f"Found {len(results)} articles for {day_offset} days ago.")

for idx, row in enumerate(results):
    user_name = row[2]
    title = row[4]
    content = row[6]
    description = row[8]
    image_url = row[7]
    category_id = row[13]

    post_time = start_time + datetime.timedelta(minutes=idx * interval)

    wp_cursor.execute(f"SELECT ID FROM wp_users WHERE user_login = '{user_name}'")
    result = wp_cursor.fetchone()
    if result:
        author_id = result[0]
    else:
        wp_cursor.execute("""
            INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_registered)
            VALUES (%s, %s, %s, %s, %s)
        """, (user_name, '123456', user_name, f"{user_name}@bobobk.com", post_time.strftime('%Y-%m-%d %H:%M:%S')))
        wp_db.commit()
        author_id = wp_cursor.lastrowid

    wp_cursor.execute("""
        INSERT INTO wp_posts (
            post_author, post_date, post_date_gmt, post_content, post_title,
            post_excerpt, post_status, comment_status, ping_status, post_name,
            post_modified, post_modified_gmt, post_type
        ) VALUES (%s, %s, %s, %s, %s, %s, 'draft', 'open', 'open', %s, %s, %s, 'post')
    """, (
        author_id, post_time, post_time, content, title,
        description, user_name, post_time, post_time
    ))
    wp_db.commit()
    post_id = wp_cursor.lastrowid

    wp_cursor.execute("""
        INSERT INTO wp_postmeta (post_id, meta_key, meta_value)
        VALUES (%s, 'featured_image', %s)
    """, (post_id, image_url))

    category_map = {
        '263': 9,
        '264': 10,
        '265': 8,
        '266': 6
    }
    taxonomy_id = category_map.get(str(category_id), 6)
    wp_cursor.execute("""
        INSERT INTO wp_term_relationships (object_id, term_taxonomy_id)
        VALUES (%s, %s)
    """, (post_id, taxonomy_id))

    wp_db.commit()
    print(f"Posted article ID {row[0]} to WordPress as post ID {post_id}")

cursor.close()
wp_cursor.close()
db.close()
wp_db.close()