首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【独立开发】马哥文案提取器:用小程序实现轻量化解析无水印视频

【独立开发】马哥文案提取器:用小程序实现轻量化解析无水印视频

作者头像
马哥python说
发布2026-07-06 17:28:40
发布2026-07-06 17:28:40
10
举报

一、开发背景

Hi,大家!我是马哥,一名10年+程序猿。

短视频内容越来越多以后,一个很常见的工作流是:看到一个不错的选题、脚本或案例,先把链接保存下来,再回头整理文案、截图、视频素材和话题标签。

如果只是偶尔保存一两条内容,手动复制当然也能完成。但当这个动作每天重复几十次,问题就会变成工程问题:链接来自不同平台,分享文本里混着口令和短链,视频和图文的媒体结构不同,移动端保存还要处理权限、进度、失败重试、防盗链等细节。

基于此出发点,我开发了一个轻量级工具:马哥文案提取器。它是一个微信小程序,核心能力是粘贴短视频或图文链接后,解析出视频、图片、文案、话题标签和基础互动数据,并提供保存、复制、收藏和历史记录等操作。

此文不讲宏大的产品方法论,只从工程角度拆一下:一个看起来简单的“粘贴链接并解析”,背后需要哪些模块配合。

二、我的设计初衷

这个需求有几个明显特点:

  • 使用场景主要发生在手机上。
  • 用户拿到的是平台分享链接,不是结构化数据。
  • 操作链路要短,最好复制链接后马上能识别。
  • 保存视频或图片需要调用系统相册能力。
  • 历史记录、文案收藏这类数据并不一定需要账号体系。

所以我一开始没有把它做成一个完整web系统,也没有先上登录、会员、支付、后台管理这些重模块,而是选了一个更短的路径:

  • 前端使用微信原生小程序。
  • 后端使用 Python FastAPI。
  • Redis只做缓存和限流。
  • 历史记录、收藏夹放在小程序本地存储。
  • 解析能力通过后端统一封装成一个接口。

这类工具型产品,早期最重要的是让核心路径足够顺:复制链接、打开、解析、保存或复制。功能少一点没关系,但每一步都要稳。

三、技术架构

3.1 整体架构

项目可以分成三层:

代码语言:javascript
复制
微信小程序
  ├─ 首页:链接输入、平台识别、解析结果、保存/复制
  ├─ 历史:最近解析记录、重新解析、复制文案
  └─ 我的:文案收藏、使用帮助、关于

FastAPI 后端
  ├─ POST /api/parse    统一解析入口
  ├─ GET  /api/proxy    媒体代理下载
  ├─ parsers/           各平台解析器
  └─ config.py          缓存、限流、下载限制

基础设施
  ├─ Redis:解析缓存、IP 限流
  └─ Caddy:HTTPS 与反向代理

小程序端只知道自己的后端地址,例如:

代码语言:javascript
复制
App({
  globalData: {
    baseUrl: 'https://api-video.mgnb.pro'
  }
})

前端不会直接请求抖音、小红书、快手或B站。所有平台差异都收敛在后端解析器里,这样以后调整某个平台的解析逻辑,不需要频繁改小程序端。

3.2 前端实现:把复杂流程收敛成一个输入框

首页的交互很简单:用户粘贴分享文本,小程序从文本中提取URL,然后识别平台。

代码语言:javascript
复制
function extractUrl(text) {
if (!text) returnnull;
var urlRegex = /(https?:\/\/[^\s<>"{}|\\^`\[\]]+)/g;
var match = text.match(urlRegex);
return match ? match[0] : null;
}

function detectPlatform(url) {
if (!url) return'';
if (/douyin\.com|iesdouyin\.com/.test(url)) return'抖音';
if (/xhslink\.com|xiaohongshu\.com/.test(url)) return'小红书';
if (/kuaishou\.com|gifshow\.com/.test(url)) return'快手';
if (/bilibili\.com|b23\.tv/.test(url)) return'哔哩哔哩';
return'';
}

真正发起解析时,前端只调用一个接口:

代码语言:javascript
复制
request('/api/parse', 'POST', { url: url })

后端返回的数据结构已经被统一过。无论原始平台是什么,前端都尽量只关心这些字段:

  • platform:平台。
  • type:视频或图文。
  • author:作者信息。
  • caption:正文文案。
  • hashtags:话题标签。
  • stats:点赞、评论、收藏、分享。
  • video:视频地址、封面、时长。
  • images:图片列表。
  • downloadable / limit:是否允许下载及限制提示。

这样的好处是,页面层不用理解每个平台自己的字段命名。平台解析器可以很复杂,但前端保持简单。

3.3 保存视频:代理、权限和进度

保存视频看起来是一个按钮,实际需要处理三件事。

第一,平台媒体资源常常有防盗链或跨域限制,直接把原始视频地址交给小程序下载并不稳定。因此后端提供了 /api/proxy,前端把媒体URL包装成代理地址:

代码语言:javascript
复制
data.video.proxy_url =
  app.globalData.baseUrl
  + '/api/proxy?url='
  + encodeURIComponent(data.video.url)
  + '&platform='
  + (data.platform || 'douyin');

第二,保存到相册要走微信授权。用户第一次保存时需要允许相册权限;如果之前拒绝过,需要引导去设置页重新打开。

第三,下载较大视频时不能让用户只看到一个卡住的按钮,所以页面里做了下载进度弹窗。

后端代理层也做了一些兼容处理:例如图片代理会根据 content-type 判断媒体类型,遇到WebP图片时会尝试转成JPEG,以提高小程序端保存的稳定性。

3.4 后端实现:一个接口分发到多个平台解析器

后端的统一入口是 POST /api/parse。流程大致如下:

代码语言:javascript
复制
读取请求体里的 url
  ↓
检查Redis限流
  ↓
按URL识别平台
  ↓
查解析缓存
  ↓
调用对应平台Parser
  ↓
统一返回ParseResult

代码结构上,后端维护了一个解析器表:

代码语言:javascript
复制
PARSERS = {
    "douyin": DouyinParser(),
    "xiaohongshu": XiaohongshuParser(),
    "kuaishou": KuaishouParser(),
    "bilibili": BilibiliParser(),
}

这样新增平台时,只需要补一个 parser,并在检测器里加 URL 规则。接口层不需要知道每个平台怎么取详情、怎么取视频、怎么解析图文。

解析结果使用统一的 ParseResult 数据模型:

代码语言:javascript
复制
@dataclass
class ParseResult:
    platform: str
    type: str
    title: str = ""
    caption: str = ""
    hashtags: list[str] = field(default_factory=list)
    author: dict = field(default_factory=dict)
    stats: dict = field(default_factory=dict)
    video: dict | None = None
    images: list[str] | None = None

这个模型很朴素,但它解决了一个关键问题:前端展示不再依赖平台原始JSON。

3.5 小红书解析的特殊性

小红书是这个项目里比较典型的例子。图文和视频笔记的数据结构不同,分享链接可能是短链,也可能带 xsec_token,视频地址还可能需要从多个位置兜底提取。

当前实现的核心路径是:

代码语言:javascript
复制
分享链接
  ↓
跟随重定向,得到真实笔记页
  ↓
提取 note_id 和 xsec_token
  ↓
调用 web feed 接口获取 note_card
  ↓
如果是视频,补抓笔记页 HTML 兜底提取原视频地址
  ↓
统一构造成 ParseResult

主接口是小红书web侧的feed接口:

代码语言:javascript
复制
POST https://edith.xiaohongshu.com/api/sns/web/v1/feed

请求体里会带上笔记ID、图片场景、xsec_source 和 xsec_token 等字段。因为这个接口需要签名和Cookie,项目里单独封装了签名逻辑,业务代码只关心“给我一个链接,返回结构化笔记数据”。

这里有一个工程上的取舍:解析器不是追求“只用一种方式拿到所有数据”,而是允许多级兜底。

例如视频地址优先从feed数据中的 originVideoKey 推导;如果拿不到,再从页面HTML中查找 masterUrl、backupUrls 等候选;最后再从note数据里挑一个质量相对更好的视频URL。

这种方式不优雅,但在面对外部平台经常变动的数据结构时,单一策略反而更脆。

3.6 缓存、限流和下载限制

这类解析服务有两个现实问题:

同一个链接可能被重复解析。 接口可能被频繁调用,甚至被刷。 因此后端使用 Redis 做两件事。

第一是缓存解析结果:

代码语言:javascript
复制
CACHE_TTL = 7200  # 2小时

同一个链接在缓存有效期内再次解析,会直接返回缓存结果,减少对外部平台的请求。

第二是IP级别限流:

代码语言:javascript
复制
RATE_LIMIT_PER_MINUTE = 10

超过限制时返回:

代码语言:javascript
复制
{
  "code": 4029,
  "message": "请求过于频繁,请稍后再试"
}

还有一个容易被忽略的细节:视频下载限制没有写死在小程序端,而是在后端配置里控制。

代码语言:javascript
复制
MAX_VIDEO_DURATION_SECONDS = 300

后端会给解析结果补充:

代码语言:javascript
复制
{
  "downloadable": false,
  "limit": {
    "message": "视频超过5分钟,暂不支持下载"
  }
}

前端保存视频时只看 downloadable 和 limit.message。这样以后调整限制,不需要重新发小程序版本。

3.7 历史记录和收藏:能本地解决的就不加系统复杂度

很多工具型产品一上来就想做账号、同步、会员、后台。这个项目没有这么做。

历史记录和文案收藏都存在小程序本地:

代码语言:javascript
复制
var HISTORY_KEY = 'parse_history';
var FAVORITE_KEY = 'favorites';
var MAX_HISTORY = 50;

每次解析成功后,把标题、平台、作者、封面、文案、时间和原始链接保存下来。历史页里可以复制文案、删除记录,也可以点击记录重新回到首页解析。

这里有个小细节:微信小程序的tab页面跳转不能直接带query参数,所以历史页重新解析时用了一个本地存储中转值:

代码语言:javascript
复制
history 页面写入 pending_parse_url
  ↓
wx.switchTab 回首页
  ↓
首页 onShow 消费 pending_parse_url
  ↓
自动填入链接并重新解析

这个方案很轻,但足够稳定。

收藏夹也是同样思路。用户看到值得保留的文案,可以点“收藏文案”,之后在“我的”页面统一复制和管理。

四、小程序部署方案

整体方案采用:Caddy + FastAPI + Redis

部署层也尽量保持简单。

当前小程序访问的是一个 HTTPS API 域名,Caddy 负责证书和反向代理:

代码语言:javascript
复制
api-video.mgnb.pro {
    reverse_proxy 127.0.0.1:8000
}

后端服务跑在本机 8000 端口,Redis 监听本地:

代码语言:javascript
复制
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379

对于这个规模的工具来说,Caddy + FastAPI + Redis 已经够用。没有MySQL,没有复杂队列,没有后台管理系统,也没有登录态同步。少一层系统,就少一类维护成本。

五、使用方法

从用户角度看,完整流程只有几步:

  1. 在抖音、小红书、快手或哔哩哔哩复制作品分享链接。
  2. 打开小程序,粘贴链接或让小程序识别剪贴板。
  3. 点击解析,等待视频、图片和文案展示。
  4. 按需复制文案、保存视频、保存图片或收藏文案。
  5. 后续可以在历史记录里重新查看或再次解析。

如果你想体验完整流程,可以在微信里搜索 “马哥文案提取器”,或点击下方卡片,立即使用:

六、使用演示视频

小程序使用演示:

七、一些开发体会

这个小程序看起来只是一个“解析链接”的工具,但开发过程中反复遇到几个典型问题。

第一,外部平台的数据结构不稳定,所以解析器要做好兜底,而不是假设某个字段永远存在。

第二,前端不要直接理解平台差异。统一数据模型比在页面里写一堆平台判断更重要。

第三,能放在后端控制的策略尽量放后端,比如下载时长限制、缓存版本、限流规则。小程序发版周期比后端重,不适合承载频繁调整的策略。

第四,不是所有数据都需要服务端持久化。历史和收藏先放本地,既保护隐私,也降低系统复杂度。

第五,工具产品的体验很大一部分来自边界处理。权限拒绝、链接无效、下载失败、缓存命中、旧历史记录缺少原始链接,这些都不是核心功能,但会决定用户觉得它“能不能用”。

八、最后总结

如果把这个项目抽象一下,它其实是一个很常见的轻量工具架构:

  • 原生小程序负责交互
  • FastAPI 负责聚合和适配
  • Redis 负责缓存和限流
  • 本地存储承接轻量个人数据
  • Caddy 负责HTTPS和反向代理

它没有做成一个庞大的系统,也没有把所有功能一次性堆上去。先把“复制链接 -> 解析 -> 保存/复制”这条链路打磨顺,再逐步补平台、补兜底、补历史收藏。

很多时候,工具型产品的关键不是功能列表有多长,而是某个高频动作能不能被稳定地缩短。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老男孩的平凡之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、开发背景
  • 二、我的设计初衷
  • 三、技术架构
    • 3.1 整体架构
    • 3.2 前端实现:把复杂流程收敛成一个输入框
    • 3.3 保存视频:代理、权限和进度
    • 3.4 后端实现:一个接口分发到多个平台解析器
    • 3.5 小红书解析的特殊性
    • 3.6 缓存、限流和下载限制
    • 3.7 历史记录和收藏:能本地解决的就不加系统复杂度
  • 四、小程序部署方案
  • 五、使用方法
  • 六、使用演示视频
  • 七、一些开发体会
  • 八、最后总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档