杆子博客

杆子博客

博采众长 一诺千金!
当前位置: 首页 > 教程 > 正文

微信公众号文章一键同步到 WordPress 完整方案

适用场景:在手机微信浏览公众号时,遇到感兴趣的文章,通过一键触发 Webhook,自动将文章内容发布到自己的 WordPress 站点,方便存档与二次阅读。


一、整体架构概览

整个系统由以下几个环节串联:

[手机微信] → 发现感兴趣文章
↓ 复制链接 / 一键分享
[iOS 快捷指令 / HTTP Shortcuts]
↓ POST 请求(附带文章 URL)
[HuggingFace Spaces - Flask Webhook 服务]
↓ 调用 wechat-article-exporter API 获取文章 HTML
↓ 替换图片地址(wsrv.nl 代理,解决防盗链)
[WordPress REST API]
↓ 创建草稿文章
[WordPress 后台] → 检查后发布

核心技术选型

环节技术方案理由
文章内容获取wechat-article-exporter 公开 API无需维护爬虫,稳定,支持 html/markdown/text
图片防盗链wsrv.nl 代理前缀免费 CDN,自动转 WebP,无需下载上传
Webhook 服务HuggingFace Spaces(Docker 模式)免费,公开 HTTPS URL,Python 全功能支持
CI/CDGitHub → Docker Hub → HF Space 自动同步预构建镜像加速 HF 启动,代码集中管理
触发方式iOS 快捷指令(Shortcuts)微信内分享即触发,2 秒完成
WordPress 发布REST API + Application Password原生支持,无需插件

二、前置准备

2.1 WordPress 开启 REST API 应用密码

  1. 登录 WordPress 后台,进入用户 → 个人资料
  2. 下滑找到应用密码区块
  3. 填写一个名称(如 wxpush),点击添加新应用密码
  4. 复制生成的密码(格式类似 xxxx xxxx xxxx xxxx xxxx xxxx),只显示一次,请妥善保存
  5. 确认站点已开启固定链接(设置 → 固定链接),REST API 才能正常工作

注意:应用密码中的空格保留即可,Python requestsHTTPBasicAuth 会自动处理。

2.2 注册所需账号


三、项目代码结构

在 GitHub 新建一个仓库,目录结构如下:

wxpush/
├── app.py                # Flask Webhook 主程序
├── requirements.txt      # Python 依赖
├── Dockerfile            # Docker 镜像定义
└── .github/
    └── workflows/
        └── deploy.yml    # GitHub Actions CI/CD 配置

四、核心代码实现

4.1 主程序 app.py

import os
import re
import requests
from flask import Flask, request, jsonify
from requests.auth import HTTPBasicAuth

app = Flask(__name__)

# 从环境变量读取配置(不要硬编码在代码里)
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "change-me")
WP_URL = os.environ.get("WP_URL", "https://your-wordpress.com")
WP_USER = os.environ.get("WP_USER", "admin")
WP_PASS = os.environ.get("WP_PASS", "")

EXPORTER_API = "https://wechat-article-exporter-lyart.vercel.app/api/public/v1/download"

def fetch_article(wx_url: str) -> dict:
    """
    通过 wechat-article-exporter 公开 API 获取文章内容。
    主路径:调用第三方 API(无需 API Key)。
    备用路径:直接 requests 抓取微信页面。
    """
    try:
        resp = requests.get(
            EXPORTER_API,
            params={"url": wx_url, "format": "html"},
            timeout=30
        )
        resp.raise_for_status()
        data = resp.json()
        return {"title": data.get("title", ""), "content": data.get("content", "")}
    except Exception:
        return fetch_article_fallback(wx_url)

def fetch_article_fallback(wx_url: str) -> dict:
    """备用抓取方式:直接解析微信文章页面 HTML"""
    from bs4 import BeautifulSoup
    headers = {
        "User-Agent": (
            "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) "
            "AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
        )
    }
    resp = requests.get(wx_url, headers=headers, timeout=20)
    soup = BeautifulSoup(resp.text, "html.parser")
    title = soup.find("h1", id="activity-name")
    content = soup.find("div", id="js_content")
    return {
        "title": title.get_text(strip=True) if title else "",
        "content": str(content) if content else "",
    }

def fix_images(html: str) -> str:
    """
    将微信图片 CDN 地址替换为 wsrv.nl 代理地址,
    解决防盗链问题,同时自动转换为 WebP 格式。
    """
    def replace_src(match):
        original_url = match.group(1)
        proxied = f"https://wsrv.nl/?output=webp&url={original_url}"
        return f'src="{proxied}"'

    # 匹配微信图片 CDN 域名
    html = re.sub(
        r'src="(https://mmbiz\.qpic\.cn[^"]*)"',
        replace_src,
        html
    )
    # 同时处理 data-src(微信懒加载图片)
    html = re.sub(
        r'data-src="(https://mmbiz\.qpic\.cn[^"]*)"',
        lambda m: f'src="https://wsrv.nl/?output=webp&url={m.group(1)}"',
        html
    )
    return html

def post_to_wordpress(title: str, content: str) -> dict:
    """通过 WordPress REST API 创建草稿文章"""
    endpoint = f"{WP_URL.rstrip('/')}/wp-json/wp/v2/posts"
    payload = {
        "title": title,
        "content": content,
        "status": "draft",
    }
    resp = requests.post(
        endpoint,
        json=payload,
        auth=HTTPBasicAuth(WP_USER, WP_PASS),
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()

@app.route("/health", methods=["GET"])
def health():
    """健康检查接口,用于确认服务是否运行"""
    return jsonify({"status": "ok"})

@app.route("/push", methods=["POST"])
def push():
    """
    Webhook 主入口。
    请求体(JSON):
    {
      "secret": "你的密钥",
      "url": "https://mp.weixin.qq.com/s/xxxxxx"
    }
    """
    data = request.get_json(silent=True)
    if not data:
        return jsonify({"error": "invalid json"}), 400

    # 验证密钥,防止他人滥用接口
    if data.get("secret") != WEBHOOK_SECRET:
        return jsonify({"error": "unauthorized"}), 401

    wx_url = data.get("url", "").strip()
    if not wx_url:
        return jsonify({"error": "missing url"}), 400

    # 1. 获取文章内容
    article = fetch_article(wx_url)

    # 2. 处理图片防盗链
    article["content"] = fix_images(article["content"])

    # 3. 发布到 WordPress
    result = post_to_wordpress(article["title"], article["content"])

    return jsonify({
        "status": "ok",
        "post_id": result.get("id"),
        "post_url": result.get("link"),
        "title": article["title"]
    })

if __name__ == "__main__":
    # HuggingFace Spaces 默认监听 7860 端口
    app.run(host="0.0.0.0", port=7860)

4.2 依赖文件 requirements.txt

flask>=3.0.0
requests>=2.31.0
beautifulsoup4>=4.12.0
lxml>=5.0.0
gunicorn>=21.0.0

4.3 Dockerfile

# 使用官方 Python 精简镜像作为基础
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 先复制依赖文件,利用 Docker 层缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制应用代码
COPY app.py .

# HuggingFace Spaces 要求以非 root 用户运行
RUN useradd -m -u 1000 appuser
USER appuser

# 暴露端口(HF Spaces 固定使用 7860)
EXPOSE 7860

# 使用 gunicorn 生产级服务器启动
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "2", "--timeout", "60", "app:app"]

五、CI/CD 配置(GitHub Actions)

5.1 配置 GitHub Secrets

在 GitHub 仓库 → Settings → Secrets and variables → Actions 中添加以下 Secret:

Secret 名称说明
DOCKER_USERNAMEDocker Hub 用户名
DOCKER_PASSWORDDocker Hub 密码或 Access Token
HF_TOKENHuggingFace Access Token(需要 write 权限)

5.2 GitHub Actions 工作流 .github/workflows/deploy.yml

name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Log in to Docker Hub
        run: |
          echo "${{ secrets.DOCKER_PASSWORD }}" | \
          docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      - name: Build and push Docker image
        run: |
          IMAGE="${{ secrets.DOCKER_USERNAME }}/wxpush:latest"
          docker build -t $IMAGE .
          docker push $IMAGE

      - name: Sync to HuggingFace Space
        env:
          HF_TOKEN: ${{ secrets.HF_TOKEN }}
          HF_USERNAME: your-hf-username
          SPACE_NAME: wxpush
        run: |
          git config user.email "[email protected]"
          git config user.name "GitHub Actions"
          git remote add hf \
          https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$SPACE_NAME
          git push hf main --force

5.3 为什么这样能加速 HuggingFace 构建?

HuggingFace Spaces Docker 模式必须通过仓库中的 Dockerfile 重新构建,无法直接运行预构建镜像。但通过将 Dockerfile 的基础镜像改写为 Docker Hub 上已预构建好的镜像,HF 构建时直接拉取镜像层,省去了重新安装所有依赖的时间:

# HuggingFace Space 仓库中的 Dockerfile(极简版)

# 直接复用 Docker Hub 上已构建好的镜像层
FROM your-dockerhub-username/wxpush:latest

# 只需做 HF 特定的配置
USER root
RUN useradd -m -u 1000 appuser 2>/dev/null || true
USER appuser

EXPOSE 7860
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "2", "--timeout", "60", "app:app"]

六、HuggingFace Spaces 部署配置

6.1 创建 Space

  1. 登录 HuggingFace,点击头像 → New Space
  2. Space name 填写 wxpush
  3. SDK 选择 Docker
  4. Visibility 选择 Private(防止 Webhook 接口被公开访问)
  5. 点击 Create Space

6.2 配置 Secrets(环境变量)

在 Space 页面 → Settings → Variables and secrets 中添加以下 Secret(选择 Secret 类型,不会明文显示):

变量名示例值说明
WEBHOOK_SECRETmy-secret-2026自定义密钥,用于验证请求合法性
WP_URLhttps://yourblog.comWordPress 站点地址
WP_USERadminWordPress 用户名
WP_PASSxxxx xxxx xxxx xxxxWordPress 应用密码

6.3 确认服务运行

Space 构建完成后,访问 https://your-hf-username-wxpush.hf.space/health,若返回 {"status": "ok"} 则服务正常。


七、手机端触发配置

7.1 iOS 快捷指令(推荐)

在 iPhone 上创建以下快捷指令,实现从微信「分享」菜单一键触发:

  1. 打开「快捷指令」App,新建快捷指令
  2. 添加动作:获取剪贴板接收来自分享表单的输入(类型选 URL)
  3. 添加动作:获取 URL 的内容
    • URL 填写:https://your-hf-username-wxpush.hf.space/push
    • 方法:POST
    • 请求体:JSON,填入:
      {
        "secret": "my-secret-2026",
        "url": "快捷指令输入变量"
      }
      
  4. 添加动作:显示通知,内容填「文章已推送到 WordPress ✅」
  5. 保存快捷指令,命名为「推送到 WP」

使用方式:在微信文章页面 → 右上角「…」→ 复制链接 → 运行快捷指令(或通过分享菜单直接触发)。

7.2 Android(HTTP Shortcuts)

  1. 安装 HTTP Shortcuts App(Google Play 免费)
  2. 新建快捷方式,类型选 Regular Shortcut
  3. URL:https://your-hf-username-wxpush.hf.space/push
  4. 方法:POST,请求体 JSON:
    {
      "secret": "my-secret-2026",
      "url": "{clipboard}"
    }
    
  5. 将快捷方式添加到桌面,微信复制链接后点击即可触发

八、关于 HuggingFace Spaces 休眠问题

免费版 HuggingFace Spaces 在约 15 分钟无请求后会进入休眠,冷启动需要 30-60 秒。对于本项目(个人偶发性推送)影响不大,因为:

  • Webhook 请求会自动唤醒 Space
  • 唤醒后再重试即可(iOS 快捷指令可加重试逻辑)

如需完全避免休眠,可考虑以下方案:

  • 升级 HF Pro 账号:支持持久运行(约 $9/月)
  • 改用 Railway 或 Render:有限免费时数,但无休眠
  • UptimeRobot 定时 Ping:每 5 分钟请求 /health 接口保持活跃(免费)

九、完整测试流程

服务部署完成后,可通过以下命令在电脑端先行测试:

# 测试健康检查
curl https://your-hf-username-wxpush.hf.space/health

# 测试推送一篇微信文章
curl -X POST https://your-hf-username-wxpush.hf.space/push \
  -H "Content-Type: application/json" \
  -d '{
    "secret": "my-secret-2026",
    "url": "https://mp.weixin.qq.com/s/你的文章ID"
  }'

成功响应示例:

{
  "status": "ok",
  "post_id": 123,
  "post_url": "https://yourblog.com/?p=123",
  "title": "文章标题"
}

然后登录 WordPress 后台 → 文章 → 草稿,即可看到刚刚同步的文章,确认内容无误后手动发布。


十、常见问题排查

Q:返回 401 Unauthorized A:检查请求中的 secret 字段是否与环境变量 WEBHOOK_SECRET 一致。

Q:WordPress 发布失败,返回 401 A:确认 WP_USERWP_PASS 正确,且 WordPress 已生成应用密码(不是登录密码)。

Q:文章内容为空 A:wechat-article-exporter API 可能暂时不可用,程序会自动切换到 Fallback 模式直接抓取;若仍为空,可能该文章需要登录才能查看。

Q:图片显示为空白 A:检查 fix_images 函数是否正确替换了 data-src 属性,部分微信文章使用懒加载图片。

Q:HuggingFace Space 无法访问 A:若 Space 设置为 Private,需要在请求头中加入 HF Token,或改为 Public 并依靠 secret 字段鉴权。


本方案代码已在 Python 3.11 + Flask 3.x 环境下验证,wechat-article-exporter API 由第三方提供,如遇 API 失效请降级使用 Fallback 抓取模式。

打赏支持
支付宝打赏 支付宝打赏
微信打赏 微信打赏

「请 GANZI 喝杯咖啡作为鼓励」~

您可能还会对这些文章感兴趣!

导航
侧边栏