开源项目从 0 到 1:我用 2 周业余时间搭建了多平台发布系统
开源项目从 0 到 1:我是怎么用 2 周业余时间搭建多平台发布系统的 起因:一个周三晚上的崩溃 故事开始于一个普通的周三晚上。 22:00,写完了一篇 Python 教程。22:35,还在复制粘贴第 6 个平台。 当时心里想的是: 「我写了 40 分钟文章,为什么还要花 30 分钟发布?」 作为一个程序员的本能反应——这个问题应该用代码解决。 Day 1-2:技术选型 动手之前花了两天调研。核心问题是: 怎么跟 9 个平台对话? 经过调研发现,平台分三类: 类型 代表 方案 有公开 API 掘金、CSDN、Dev.to HTTP 请求直接调 有半公开 API 知乎、博客园 需要逆向签名(x-zse-96、X-Ca) 没有 API 小红书、抖音 只能 Playwright 浏览器模拟 技术栈决策: 后端: Python + FastAPI - 选 Python 因为 Playwright 的 Python 绑定最成熟 - 选 FastAPI 因为异步支持好,自带文档 前端: 原生 HTML/CSS/JS - 不需要 React/Vue。一个管理后台而已,原生 JS 完全够用 - 零 npm 依赖,部署简单 数据库: SQLite - 单文件,不需要装 MySQL/PostgreSQL - 个人工具,并发不是问题 浏览器插件: Chrome Extension Manifest V3 - 一键提取 Cookie,用户不用手动 F12 不做的事情: ❌ 不用 Docker(增加复杂度,个人部署不需要) ❌ 不用 Redis(SQLite 足够) ❌ 不用前端框架(过度设计) Day 3-5:搭建骨架 先搭最小可用版本: 一个 API + 一个页面 + 一个平台 。 选掘金作为第一个平台——它有公开 API,最简单。 # 核心抽象:每个平台一个 Adapter class BasePlatform : async def publish ( self , title , content , tags , credentials ) -> PublishResult : raise NotImplementedError async def fetch_stats ( self , post_id , credentials ) -> StatsResult : raise NotImplementedError 这个阶段的关键决策: 策略模式 :每个平台是一个独立的 Adapter 类,新增平台不改核心逻辑 异步优先 :所有网络请求用 async/await,发布 9 个平台可以并发 失败隔离 :一个平台失败不影响其他平台 骨架搭完后,掘金能发了。但只有一个平台没意义——得把所有平台都接上。 Day 6-10:攻克硬骨头 知乎:逆向 x-zse-96 签名 知乎的 API 要求每个请求带一个 x-zse-96 签名头。签名的生成逻辑藏在知乎前端的混淆 JS 里。 花了两个晚上逆向:原来是 MD5(特定参数 + d_c0 Cookie) → AES-CBC 加密 → 拼接前缀 。 def _make_x_zse_96 ( url : str , d_c0 : str ) -> str : source = f " 101_3_3.0+/api/v4/ { url } + { d_c0 } " md5_hash = hashlib . md5 ( source . encode ()). hexdigest () # ... AES-CBC 加密 ... return " 2.0_ " + encrypted . hex () 搞定的那一刻,知乎文章秒发——那种感觉比写完代码还爽。 CSDN:阿里云网关签名 CSDN 用的是阿里云 API 网关的 HMAC-SHA256 签名方案。文档是中文的,但签名串的格式写得很隐晦——分隔符必须是 \n ,不能是空格。这个细节浪费了我两个小时。 博客园:2002 年的 XML-RPC 博客园的 API 是 MetaWeblog XML-RPC——一个 2002 年的协议。Python 标准库自带的 xmlrpc.client 直接用。但有个坑:分类里不加 [Markdown] ,文章就会被当 HTML 解析。 小红书:Playwright 兜底 小红书没有公开 API,只能上 Playwright。但每次启动浏览器要 2-3 秒。优化手段: Cookie 持久化,跳过登录 先通过 API 拿到图片上传地址,只让 Playwright 做最后的表单提交 在 Linux 服务器上用 Xvfb 虚拟显示 Day 11-14:打磨产品 核心功能跑通后,开始打磨: 数据看板 :每个平台的阅读/点赞/评论自动汇