正在整理这一页的内容…
Project Detail
从内容下载到 AI 识别:我的小红书内容提取服务
这篇笔记记录一下我用 Python 改造一个小红书内容获取项目的过程。 事情的起因比较直接。 我希望输入一条小红书链接以后,程序可以自动拿到这篇内容里的信息: 标题和作者等基本数据。 正文文案。 图片。 视频。 图片中出现的文字。 视频语音中说过的所有内容。 我在 GitHub 上找到过一个开源项目,它已经可以解析链接

这篇笔记记录一下我用 Python 改造一个小红书内容获取项目的过程。
事情的起因比较直接。
我希望输入一条小红书链接以后,程序可以自动拿到这篇内容里的信息:
- 标题和作者等基本数据。
- 正文文案。
- 图片。
- 视频。
- 图片中出现的文字。
- 视频语音中说过的所有内容。
我在 GitHub 上找到过一个开源项目,它已经可以解析链接并下载图片、视频。
这个项目解决了最基础的问题:
把内容保存下来。但我真正想要的不只是下载。
我还希望程序能继续理解下载下来的内容:
图片里写了什么?
视频里说了什么?
一篇内容完整表达了什么?所以我在原项目基础上增加了本地图片文字识别、视频音频分离和语音转文字,最后再把这些能力精简成一个 FastAPI 服务。
先说结论
这个项目最后形成的处理流程大概是:
输入小红书链接
-> 解析内容基本信息
-> 获取正文、图片和视频地址
-> 下载需要处理的媒体文件
-> 对图片执行 OCR
-> 从视频中分离音频
-> 对音频执行语音识别
-> 整理成统一的结构化结果
-> 通过 FastAPI 返回最开始的开源项目更像下载工具。
改造以后,它更接近一个内容理解服务:
下载只是中间步骤,
最终目标是把图文和视频内容转换成程序可以继续处理的文字数据。原项目已经解决了什么
从零开始解析一个内容平台并不轻松。
现有开源项目已经帮我解决了不少基础问题:
- 接收分享链接。
- 找到真正的内容地址。
- 解析笔记基本信息。
- 取得正文文案。
- 获取图片地址。
- 获取视频地址。
- 把图片或视频下载到本地。
如果只做一个素材下载器,这些能力已经够用了。
但下载完成后,程序拿到的仍然主要是媒体文件:
image_1.jpg
image_2.jpg
video.mp4对于人来说,打开图片和视频就能理解内容。
对于后面的程序、搜索系统或 AI 工作流来说,这些文件还不够方便。
它们更希望拿到:
图片文字
视频字幕
完整文案
结构化元数据所以我的改造重点不是重新做一套下载逻辑,而是在下载结果后面继续增加内容识别流程。
为什么正文文案还不够
小红书内容里的信息不一定都写在正文中。
有些笔记的正文可能只有一句:
具体步骤都放在图片里了。真正有价值的内容却在多张图片中。
比如:
- 教程步骤。
- 产品参数。
- 价格和活动规则。
- 菜谱配料。
- 旅行路线。
- 截图中的聊天记录。
视频内容也是一样。
发布者可能没有把完整口播写进正文,重要信息只存在于视频语音里。
如果程序只提取正文,就会漏掉很大一部分内容。
所以一篇笔记的完整文字信息,应该粗略理解成:
正文文案
+ 图片里的文字
+ 视频语音里的文字图片里的文字怎么提取
图片文字提取使用的是 OCR。
OCR 的作用可以简单理解成:
输入图片
-> 找出图片中的文字区域
-> 识别文字内容
-> 返回文本和位置等信息项目拿到图片后,会逐张交给本地 OCR 模型处理:
image_1.jpg -> OCR -> 第一张图片的文字
image_2.jpg -> OCR -> 第二张图片的文字
image_3.jpg -> OCR -> 第三张图片的文字最后按原来的图片顺序整理。
结果可以设计成:
{
"images": [
{
"url": "https://example.com/image-1.jpg",
"text": "第一张图片识别出的文字"
},
{
"url": "https://example.com/image-2.jpg",
"text": "第二张图片识别出的文字"
}
]
}保留图片顺序很重要。
很多图文教程是按顺序表达的。如果只把所有 OCR 结果混成一段文字,就不知道某句话原来属于哪张图,也容易打乱上下文。
为什么使用本地 AI 做 OCR
图片可以上传到第三方识别接口,但我更希望把识别放在本地。
这样做主要有几个原因:
- 不需要为每张图片调用外部 API。
- 减少图片上传到第三方服务。
- 批量处理时成本更容易控制。
- 模型和处理流程可以自己调整。
- 没有外部识别服务时也能运行。
本地模型也有自己的成本:
- 第一次加载模型需要时间。
- 模型会占用内存或显存。
- 不同图片质量会影响准确率。
- 倾斜文字、艺术字体和低清截图可能识别不准。
所以我不会把 OCR 理解成百分之百准确的“读取”。
它更像是:
尽量把图片里的视觉文字转换成可搜索、可分析的文本。OCR 模型不要每次请求都重新加载
把本地 AI 接进 API 时,有一个很实际的问题:
模型应该什么时候初始化?如果每次请求进来都重新加载 OCR 模型:
def recognize_image(path: str):
model = load_ocr_model()
return model.predict(path)接口会非常慢,还会反复占用资源。
更合理的方式是在服务启动时初始化一次,然后复用:
ocr_model = load_ocr_model()
def recognize_image(path: str):
return ocr_model.predict(path)如果模型本身不支持多个线程同时调用,还要限制并发,或者给推理过程加锁。
服务化以后,问题不再只是“模型能不能识别”,还包括:
模型怎么复用?
同时来多个请求怎么办?
内存能不能撑住?
失败以后会不会影响下一次请求?这些都是从脚本变成服务以后才会明显出现的问题。
视频文字不能直接从视频文件里拿
视频里的文字来源至少有两种:
- 画面中显示的字幕和文字。
- 人说话产生的语音内容。
这次我主要处理的是第二种,也就是把视频语音转换成文字。
流程是:
下载视频
-> 从视频中分离音频
-> 把音频交给语音识别模型
-> 得到视频口播文字视频文件里通常同时包含画面和音频轨道。
语音识别模型不需要处理画面,所以先把音频单独提取出来,会让后面的处理更清楚。
从视频中分离音频
视频音频分离可以交给 FFmpeg。
例如:
ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 output.wav这里大概表示:
-i input.mp4:输入视频。-vn:不要视频画面。-ac 1:转成单声道。-ar 16000:采样率调整为 16000Hz。output.wav:输出音频文件。
具体参数要看语音识别模型的输入要求。
Python 里可以通过 subprocess 调用:
import subprocess
def extract_audio(video_path: str, audio_path: str):
subprocess.run(
[
"ffmpeg",
"-y",
"-i",
video_path,
"-vn",
"-ac",
"1",
"-ar",
"16000",
audio_path,
],
check=True,
capture_output=True,
)这里的 check=True 很重要。
如果 FFmpeg 执行失败,Python 会明确抛出异常,不会让后面的语音识别继续拿一个不存在或损坏的音频文件运行。
用 AI 提取音频里的文字
音频准备好以后,再交给语音识别模型。
这个过程也叫 ASR:
Automatic Speech Recognition大概流程是:
output.wav
-> 本地语音识别模型
-> 视频口播文本结果可以先保存成一整段:
{
"video": {
"url": "https://example.com/video.mp4",
"transcript": "这是视频中识别出的完整口播内容"
}
}如果模型能返回时间戳,还可以进一步保存分段:
{
"segments": [
{
"start": 0.0,
"end": 4.2,
"text": "今天分享一个简单的方法"
},
{
"start": 4.2,
"end": 9.8,
"text": "第一步先准备需要的材料"
}
]
}有时间戳以后,后面可以继续做:
- 生成字幕。
- 定位某句话在视频中的位置。
- 按段总结。
- 给视频建立全文搜索。
- 提取章节和重点。
视频里的“所有文案”要怎么理解
严格来说,视频中的文案不一定只有语音。
还可能包括:
- 画面字幕。
- 标题卡片。
- 产品包装文字。
- 评论截图。
- 没有念出来的补充说明。
语音识别只能拿到“说出来的内容”。
如果以后希望更完整,可以继续增加视频抽帧 OCR:
每隔一段时间抽取一帧
-> 检测画面文字
-> 对相邻重复字幕去重
-> 和语音识别结果合并不过视频抽帧会明显增加计算量。
同一句字幕可能连续出现很多帧,还需要做去重和时间对齐。
所以第一版先做好音频转写,是比较合理的范围。
把不同内容整理成统一结果
正文、图片 OCR 和视频转写来自不同处理流程。
如果每部分各自返回一套格式,调用方会很难使用。
所以我希望 API 最后返回统一的结构:
{
"title": "内容标题",
"author": {
"name": "作者名称"
},
"description": "发布者填写的正文文案",
"images": [
{
"url": "图片地址",
"text": "图片 OCR 文本"
}
],
"video": {
"url": "视频地址",
"transcript": "视频语音识别文本"
},
"full_text": "正文、图片文字和视频文字合并后的内容"
}这里的 full_text 很有用。
后面的 AI、搜索系统或知识库不一定关心文字原来来自图片还是视频,它们可以先直接使用完整文本。
同时保留 images 和 video 的独立结果,又能让调用方追溯来源。
为什么最后做成 FastAPI 服务
最开始的开源项目更像一个本地脚本。
可能是这样调用:
python main.py "小红书链接"脚本适合自己测试,但不方便接入其他系统。
比如后面我可能希望:
- 网页前端直接调用。
- AI 工作流把链接交给它处理。
- 其他 Python 项目通过 HTTP 使用。
- 批量任务统一提交。
- 把它部署到一台有本地模型的机器上。
所以我把核心能力精简成了 FastAPI 服务。
接口可以设计成:
POST /extract
Content-Type: application/json
{
"url": "小红书分享链接"
}FastAPI 接收链接后,执行完整处理流程,再返回结构化结果。
FastAPI 接口大概怎么写
可以先定义请求和响应模型:
from pydantic import BaseModel, HttpUrl
class ExtractRequest(BaseModel):
url: HttpUrl
class ImageResult(BaseModel):
url: str
text: str
class VideoResult(BaseModel):
url: str
transcript: str
class ExtractResult(BaseModel):
title: str
description: str
images: list[ImageResult]
video: VideoResult | None = None
full_text: str接口只负责接收参数和返回结果:
from fastapi import FastAPI
app = FastAPI()
@app.post("/extract", response_model=ExtractResult)
async def extract_content(request: ExtractRequest):
return await content_service.extract(str(request.url))真正的解析、下载、OCR 和语音识别逻辑放进单独的 service。
这样 API 层不会塞满所有实现细节。
同步工具放进异步接口时要小心
FastAPI 支持异步接口:
async def extract_content(...):
...但本地 OCR、FFmpeg 和语音识别不一定是异步工具。
如果在异步接口中直接运行耗时同步函数,可能会阻塞事件循环。
对于主要等待文件和外部进程的同步操作,可以考虑:
result = await asyncio.to_thread(
recognize_image,
image_path,
)不过本地 AI 推理可能是 CPU 或 GPU 密集型任务。
这时不能只认为包一层 to_thread 就万事大吉,还要考虑:
- 模型是否线程安全。
- GPU 是否能同时处理多个任务。
- 是否需要任务队列。
- 是否限制 API 并发。
- 单次请求最长能运行多久。
如果一条视频需要处理几分钟,直接让普通 HTTP 请求一直等待,也不一定是最佳方案。
后续可以改成任务式接口:
提交任务
-> 返回 task_id
-> 后台处理
-> 查询处理进度
-> 获取最终结果临时文件要统一管理
处理过程中会产生不少临时文件:
下载的图片
下载的视频
分离出的音频
模型产生的中间文件如果每次请求都保存下来但不清理,磁盘很快就会堆满。
所以我会给每个请求创建独立临时目录:
temp/
request_a/
request_b/处理完成后统一删除。
Python 可以使用临时目录:
from tempfile import TemporaryDirectory
def process_content(content):
with TemporaryDirectory() as temp_dir:
# 下载、识别和转写
...
# 离开 with 后自动清理如果处理失败,也要走清理逻辑。
不能只在成功路径里删除文件。
失败要能看出失败在哪一步
这条链路比较长:
链接解析
-> 内容请求
-> 媒体下载
-> 图片 OCR
-> 视频下载
-> FFmpeg 分离音频
-> 语音识别任何一步都可能失败。
如果最后只返回一句:
处理失败排查会很困难。
所以错误最好能区分阶段:
链接无效
内容不存在或无法访问
图片下载失败
OCR 模型执行失败
视频下载失败
FFmpeg 不可用
音频提取失败
语音识别失败日志里还应该带上请求 ID,方便把同一次请求的所有处理记录串起来。
不过日志不要输出 Cookie、Token 或完整敏感请求头。
为什么要把原项目精简
开源项目通常会包含很多完整工具需要的功能:
- 命令行交互。
- 批量下载。
- 文件命名。
- 本地目录管理。
- 多种下载选项。
- 日志输出。
- 用户配置。
这些功能对一个独立下载工具有价值。
但做成 API 服务时,不一定全都需要。
我最后更想保留的是核心链路:
输入链接
-> 得到结构化内容其他能力尽量变成内部实现,不暴露给调用方。
这样接口会更简单:
调用方不需要知道文件下载到哪里,
也不需要知道 OCR 和语音识别用了什么模型。它只需要提交链接,然后拿结果。
这个项目可以用在哪里
内容变成结构化文字以后,可以继续做很多事情:
- 自动总结一篇图文或视频内容。
- 提取教程步骤。
- 建立个人内容收藏库。
- 给图片和视频建立全文搜索。
- 把内容导入知识库。
- 分析标题、正文和口播的表达方式。
- 给 AI 工作流提供完整上下文。
下载媒体只是把文件拿到本地。
OCR 和语音识别则让文件里的信息真正进入后续的数据处理流程。
使用时要注意边界
这类工具虽然方便,也要注意使用边界。
我会把它定位为:
处理自己有权访问、保存和分析的公开内容,
用于个人整理、学习或经过授权的业务场景。不应该用它绕过登录、权限控制或平台限制,也不应该未经允许批量搬运他人的内容。
图片、视频和文案都可能受到著作权保护。
即使技术上可以获取,也不代表可以随意转载、公开发布或商业使用。
另外,平台页面结构和接口随时可能变化。
从开源项目改造而来的解析逻辑,也需要做好失效和维护的准备。
我最后想通的地方
这个项目最开始只是一个下载工具。
输入链接以后,它可以把图片和视频保存下来。
但我真正想要的是:
让程序知道这篇内容到底说了什么。所以后面的改造都是围绕这个目标展开:
正文解析负责拿到作者写出的文字
OCR 负责拿到图片里的文字
音频分离负责从视频中取出声音
语音识别负责拿到视频里说出的文字
FastAPI 负责把这些能力变成统一服务做完以后,它已经不只是“输入链接,下载文件”。
更像是:
输入一条内容链接,
输出一份尽量完整的结构化文字资料。我觉得这次改造最有价值的地方,不是又接了几个 AI 模型。
而是把原来分散的能力串成了一条完整链路:
内容获取
-> 媒体处理
-> 多模态识别
-> 结构化整理
-> API 服务化原来的开源项目解决了“怎么拿下来”。
我增加的部分解决了“拿下来以后怎么读懂它”。