如果你最近刷短视频平台,大概率会看到一类把《我的世界》(Minecraft)里的末影人与“奶龙”形象混剪在一起的速通切片,画面荒诞、台词反常规,配合游戏内的高能时刻,让人忍不住反复看几遍。这类视频本质上是“游戏速通高光”与“网络梗文化”结合的产物,热度来得快去得也快,但顺着这个热点去分析数据、做自动切片处理,却能练出一套非常完整的 Python + FFmpeg 实战能力。本文就以“末影奶龙速通切片”为切入点,从热点视频数据抓取、弹幕热词统计到本地视频自动切片,带你完整走一遍从素材采集到结果输出的技术流程。
不管你是刚接触 Python 的初学者,还是已经写过爬虫、想尝试视频处理的项目开发者,这篇教程都能给你一套能直接运行的代码脚本,也能帮你避开 B 站数据接口、FFmpeg 切片过程中常见的坑。下面我们直接开始。
1. 背景与核心概念
1.1 什么是速通切片与“末影奶龙”
速通本身就是游戏圈里一个成熟玩法,玩家利用游戏机制、BUG 和极限操作,在尽可能短的时间内通关。速通切片则是把速通过程中失误、高光、离谱场面剪出来做成几十秒的短视频。由于速通视频天然存在“紧张、反转、爆笑”的戏剧结构,切片内容很容易在短视频平台获得高播放量。
“末影奶龙”不是一个具体游戏角色,而是网络二次创作文化中的梗:末影人来自《我的世界》,特征是黑色皮肤、紫色眼睛、会瞬移;奶龙则是近年来短视频平台流行的卡通 IP,以呆萌外形和无厘头台词走红。“末影奶龙”就是把这两个毫无关联的形象拼在一起,再配合速通场景剪辑出荒诞效果。这类视频的传播量很大,但它的制作套路并不复杂,核心就是把游戏中的关键画面与热点梗结合。
1.2 为什么值得用技术手段去分析和处理这类内容
如果你是内容创作者,看到“末影奶龙”这类热点时,第一反应可能是模仿制作,但盲猜热点很容易扑空。更可靠的方法是记录同类型视频的播放量、点赞数、弹幕关键词,判断哪些文案和画面真正引发观众讨论,然后用脚本批量下载素材、切出高能片段,再人工选择包装方向。
从技术学习角度看,这个场景覆盖了三条主线知识:数据采集(B站公开接口调用)、文本分析(弹幕分词与词频统计)、视频处理(FFmpeg 切片)。这三条能力恰好是爬虫、数据分析、音视频处理三个方向最常见的入门实践点。这篇文章不会停留在讲解概念,而是直接把这三条线串成一个可运行的小型项目。
1.3 项目整体思路
我们先明确最终要做出的效果:输入一个热点关键词(比如“末影奶龙”),脚本自动完成以下步骤:
- 调用 B 站公开搜索接口,抓取与该关键词相关的视频列表,保存为结构化数据。
- 从视频信息中提取弹幕接口所需的 cid,下载弹幕 XML 文本,清洗后做分词统计,输出热点弹幕词。
- 对本地已下载的视频文件,根据时间点批量执行 FFmpeg 切片,输出视频片段。
整个流程拆成三个独立模块,代码结构清晰,每部分都可以单独复用。下面先介绍环境准备。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
2.1 运行时环境
本文示例代码在 Windows 11 + Python 3.10 环境下编写和验证。如果你使用 macOS 或 Linux,语法完全一致,只是 FFmpeg 的安装方式不同。
建议使用虚拟环境隔离依赖,避免污染全局 Python 环境。创建虚拟环境的命令如下:
bash复制python -m venv video_env
Windows 进入虚拟环境:
bash复制video_env\Scripts\activate
macOS / Linux 进入虚拟环境:
bash复制source video_env/bin/activate
2.2 Python 依赖库
需要安装的库有 requests、pandas、jieba。其中 requests 用于发送 HTTP 请求,pandas 用于数据整理与落盘,jieba 用于弹幕文本分词。如果需要生成词云图,可以额外安装 wordcloud 和 matplotlib,但这两者不是必须的。
安装命令:
bash复制pip install requests pandas jieba wordcloud matplotlib
版本不需要刻意固定为某个最新版,使用 pip 默认安装即可。不同版本之间的接口变化不会影响本文代码的核心用法。
2.3 FFmpeg 安装
FFmpeg 是视频切片的核心工具。它不是一个 Python 库,而是一个独立的命令行工具。
Windows 用户推荐从 FFmpeg 官网下载 builds 版本,解压后将 bin 目录加入系统 PATH 环境变量,或者直接在代码里指定 ffmpeg.exe 的完整路径。
macOS 用户可以使用 Homebrew 安装:
bash复制brew install ffmpeg
Ubuntu / Debian 用户可以使用 apt 安装:
bash复制sudo apt update
sudo apt install ffmpeg
安装后可以在终端验证版本:
bash复制ffmpeg -version
只要能看到版本号输出,说明安装成功。需要注意版本差异:较新版本 FFmpeg 对部分编码参数有调整,但本文使用的基础切片命令在所有主流版本上都通用。
2.4 项目目录结构
建议按下面的结构组织代码,方便后续扩展:
code复制video_project/
├── src/
│ ├── bili_search.py
│ ├── danmaku_analysis.py
│ └── video_cut.py
├── data/
│ ├── videos.csv
│ └── danmaku.csv
├── output/
│ └── cuts/
└── input/
└── speedrun.mp4
src 目录存放 Python 代码,data 目录存放抓取结果,output 目录存放切片产物,input 目录存放本地待处理视频文件。这样即使后续数据量变大,也不会把项目根目录搞乱。
3. 核心接口与处理原理
3.1 B站公开搜索接口的调用方式
B站目前有一套公开的 Web 接口,通过 GET 请求可以搜索视频信息。接口路径以 api.bilibili.com/x/web-interface/search/type 为例,核心参数是 search_type=video、keyword=关键词、page=页码。
使用这个接口时需要带上 User-Agent 和 Referer 请求头,否则容易触发风控。返回的 JSON 数据中,data.result 是视频列表数组,每项包含视频标题、bvid、播放量、点赞数、弹幕数、作者等字段。标题字段中可能带有高亮 HTML 标签,需要做文本清理。
需要特别说明:B站公开接口的字段和风控策略会不定期调整,本文代码展示的是通用思路,如果接口返回结构变了,需要根据实际的 JSON 结构做字段适配。这个知识点本身就是爬虫开发中的常态,没有一成不变的代码。
3.2 弹幕文本获取与解析
B站弹幕需要两个步骤。第一步是根据视频 bvid 获取 cid,调用 x/player/pagelist 接口即可。第二步是请求弹幕 XML 文件,URL 规则为 https://comment.bilibili.com/{cid}.xml。
弹幕 XML 的每一行是一条弹幕,核心内容是 <d> 标签内部的文本。可以使用正则表达式提取,也可以使用 xml.etree.ElementTree 解析。考虑到弹幕文本可能存在转义字符,正则方式虽然简单,但对特殊字符处理不够优雅,更推荐用 XML 解析器。
不过对于快速获取文本内容而言,正则仍然是效率最高的方案。实际项目中可以先用正则提取,再通过字符串清洗函数去掉多余空格和换行。
3.3 FFmpeg 切片原理
FFmpeg 切片就是把一个长视频中的某一段截取出来。最常用的参数是 -ss 指定开始时间,-t 指定持续时间。
直接复制流命令:
bash复制ffmpeg -ss 00:01:23 -t 10 -i input.mp4 -c copy output.mp4
这里 -c copy 表示不重新编码,速度快,但切割点可能不够精确,有概率出现黑屏或花屏。如果需要精确到帧的切割,应该改用重新编码:
bash复制ffmpeg -ss 00:01:23 -t 10 -i input.mp4 -c:v libx264 -c:a aac output.mp4
重新编码速度慢,但兼容性更好。在批量切片场景中,如果对画质要求较高,推荐重新编码;如果只是快速预览,可以使用 -c copy。
3.4 分词统计的实现思路
jieba 是 Python 生态里使用最广泛的中文分词库。对弹幕文本调用 jieba.lcut() 会返回一个词语列表,再用 collections.Counter 统计词频,最终输出 Top N 关键词。
实际弹幕中存在大量“哈哈哈”“666”这类无意义词,需要准备一个停用词表过滤。同时要把长度小于 2 的字过滤掉,避免单个汉字干扰统计结果。
4. 完整实战:构建热点视频分析处理流水线
这部分我们分三个模块来实现。先抓取视频列表,再分析弹幕热词,最后对本地视频做自动切片。每个模块都是独立脚本,可以直接运行,也可以组合到主流程中。
4.1 模块一:搜索热点视频并保存数据
创建一个文件 src/bili_search.py,代码如下:
python复制# 文件路径:src/bili_search.py
import requests
import json
import time
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Referer": "https://www.bilibili.com/"
}
def clean_title(title: str) -> str:
"""清理标题中的高亮标签"""
return title.replace('<em class="keyword">', '').replace('</em>', '')
def search_video(keyword: str, page: int = 1):
"""搜索B站视频,返回视频列表。"""
api = "https://api.bilibili.com/x/web-interface/search/type"
params = {
"search_type": "video",
"keyword": keyword,
"page": page,
}
resp = requests.get(api, params=params, headers=HEADERS, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get("code") != 0:
print("接口返回异常:", data.get("message"))
return []
result = data.get("data", {}).get("result", [])
videos = []
for item in result:
videos.append({
"标题": clean_title(item.get("title", "")),
"bvid": item.get("bvid", ""),
"播放量": item.get("play", 0),
"点赞数": item.get("like", 0),
"弹幕数": item.get("video_review", 0),
"作者": item.get("author", ""),
"简介": item.get("description", "")
})
return videos
if __name__ == "__main__":
keyword = "末影奶龙"
all_videos = []
for page in range(1, 4):
print(f"正在抓取第 {page} 页……")
videos = search_video(keyword, page)
if not videos:
break
all_videos.extend(videos)
time.sleep(1)
print(f"共获取 {len(all_videos)} 条视频")
with open("../data/videos.json", "w", encoding="utf-8") as f:
json.dump(all_videos, f, ensure_ascii=False, indent=2)
# 转成 CSV 便于后续用 pandas 分析
import pandas as pd
df = pd.DataFrame(all_videos)
df.to_csv("../data/videos.csv", index=False, encoding="utf-8-sig")
print("数据已保存到 ../data/videos.csv")
运行脚本:
bash复制python src/bili_search.py
如果接口正常,控制台会输出抓取到的视频条数,并在 data 目录下生成 videos.json 和 videos.csv 两个文件。
代码中有几个地方需要特别说明。一是请求头必须设置 Referer,B站部分接口会校验来源;二是抓取多页时建议间隔 1 到 2 秒,降低对服务端的压力;三是 CSV 使用 utf-8-sig 编码,可以直接用 Excel 打开而不会出现中文乱码。
如果搜索结果为空,优先检查关键词是否正确,以及是否触发了风控返回码 -412。此时可以降低抓取速度,或者手动在浏览器中打开 B站搜索页,再复制 Cookie 到代码中。
4.2 模块二:下载弹幕并统计热词
创建一个文件 src/danmaku_analysis.py,这个脚本会读取 videos.json,对前若干条视频抓取弹幕,然后做分词统计。
python复制# 文件路径:src/danmaku_analysis.py
import requests
import re
import pandas as pd
import json
from collections import Counter
import jieba
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Referer": "https://www.bilibili.com/"
}
STOP_WORDS = {
"哈哈哈", "哈哈", "666", "啊啊", "真的", "什么", "怎么",
"不会", "不是", "就是", "这个", "那个", "可以", "我们",
"一个", "没有", "还是", "你们", "自己", "知道"
}
def get_cid(bvid: str) -> int:
"""根据 bvid 获取视频 cid"""
api = "https://api.bilibili.com/x/player/pagelist"
resp = requests.get(api, params={"bvid": bvid}, headers=HEADERS, timeout=10)
data = resp.json()
if data.get("code") != 0:
raise RuntimeError(f"获取 cid 失败:{data.get('message')}")
return data["data"][0]["cid"]
def fetch_danmaku(cid: int) -> list:
"""根据 cid 获取弹幕文本列表"""
url = f"https://comment.bilibili.com/{cid}.xml"
resp = requests.get(url, headers=HEADERS, timeout=10)
resp.encoding = "utf-8"
texts = re.findall(r'<d p="[^"]*">(.*?)</d>', resp.text)
# 简单清洗 HTML 实体
texts = [t.replace("&", "&").replace("<", "<").replace(">", ">") for t in texts]
return texts
def analyze_danmaku(texts: list, top_n: int = 20) -> list:
"""分词并统计词频"""
words = []
for text in texts:
words.extend(jieba.lcut(text))
filtered = []
for word in words:
word = word.strip()
if len(word) < 2:
continue
if word in STOP_WORDS:
continue
if word.isdigit():
continue
filtered.append(word)
counter = Counter(filtered)
return counter.most_common(top_n)
if __name__ == "__main__":
with open("../data/videos.json", "r", encoding="utf-8") as f:
videos = json.load(f)
all_danmaku = []
limited = videos[:5]
for idx, video in enumerate(limited):
print(f"正在处理第 {idx + 1} 条视频:{video['标题']}")
try:
cid = get_cid(video["bvid"])
texts = fetch_danmaku(cid)
print(f"获取到 {len(texts)} 条弹幕")
all_danmaku.extend(texts)
except Exception as exc:
print(f"处理失败:{exc}")
import time
time.sleep(1)
print(f"总计获取 {len(all_danmaku)} 条弹幕")
# 保存弹幕原始数据
df = pd.DataFrame({"弹幕": all_danmaku})
df.to_csv("../data/danmaku.csv", index=False, encoding="utf-8-sig")
# 输出热词
result = analyze_danmaku(all_danmaku)
print("\n弹幕热词 TOP 20:")
for word, count in result:
print(f"{word}: {count}")
运行脚本:
bash复制python src/danmaku_analysis.py
脚本默认只处理前 5 条视频,这是为了避免请求量过大。如果你希望处理全部视频,可以把 limited = videos[:5] 改成 limited = videos。
这个模块的输出非常直观,可以看到“末影奶龙”关联弹幕里真正高频的词是哪些。比如你可能看到“哈哈”“离谱”“太快了”这类情绪词,也可能看到“末影”“奶龙”“速通”等主题词。这些词可以直接用于内容选题参考,比单纯看播放量更有参考价值。
需要注意,弹幕 XML 接口偶尔会返回空文件。一个常见原因是视频没有开放弹幕,另一个原因是 cid 已经更新。遇到这种情况,脚本会通过异常捕获跳过该视频,不影响后续处理。
4.3 模块三:本地视频自动切片
当我们从热点视频中确定要复刻的片段后,往往需要从本地速通录屏中切出对应片段。创建 src/video_cut.py,代码实现一个可复用的切片函数和批量处理入口。
python复制# 文件路径:src/video_cut.py
import subprocess
import os
from pathlib import Path
FFMPEG_BIN = "ffmpeg" # 如果 ffmpeg 不在 PATH,改为完整路径,如 "D:/ffmpeg/bin/ffmpeg.exe"
def cut_video(src: str, start: str, duration: str, output: str,
reencode: bool = False) -> None:
"""
截取视频片段
:param src: 输入视频路径
:param start: 开始时间,格式 HH:MM:SS 或 秒数
:param duration: 持续时长,格式 HH:MM:SS 或 秒数
:param output: 输出文件路径
:param reencode: 是否重新编码。True 精确但慢,False 快速复制
"""
cmd = [FFMPEG_BIN]
if reencode:
cmd += ["-ss", start, "-t", duration, "-i", src,
"-c:v", "libx264", "-c:a", "aac", "-y", output]
else:
# 注意:-ss 放在 -i 之前表示快速定位,但关键帧对齐可能不准
cmd += ["-ss", start, "-t", duration, "-i", src,
"-c", "copy", "-y", output]
print("执行命令:", " ".join(cmd))
subprocess.run(cmd, check=True)
if __name__ == "__main__":
# 批量切片任务:每个任务包含开始时间、持续时长、输出文件名
tasks = [
{"start": "00:01:23", "duration": "00:00:10", "output": "cut_01.mp4"},
{"start": "00:05:47", "duration": "00:00:15", "output": "cut_02.mp4"},
{"start": "00:12:30", "duration": "00:00:08", "output": "cut_03.mp4"},
]
input_video = "../input/speedrun.mp4"
output_dir = Path("../output/cuts")
output_dir.mkdir(parents=True, exist_ok=True)
for task in tasks:
output_path = output_dir / task["output"]
cut_video(
src=input_video,
start=task["start"],
duration=task["duration"],
output=str(output_path),
reencode=True # 推荐重编码,避免黑屏
)
print("全部切片完成")
运行脚本前,先把需要切片的速通视频命名为 speedrun.mp4,放到 input 目录下。然后运行:
bash复制python src/video_cut.py
脚本会依次生成三个视频片段,保存在 output/cuts 目录中。
这里重点解释为什么默认把 reencode 参数设为 True。使用 -c copy 直接复制流虽然速度极快,但它只能在关键帧处切割。如果你的开始时间落入了两个关键帧之间,切割出的视频开头会出现黑屏或画面停滞。重新编码则每一帧都会被重新处理,切出来的时间点非常精确。
对于短视频创作来说,内容和画面精度很重要,所以我建议牺牲一点处理速度换取输出质量。
4.4 运行效果验证
完整运行三个模块后,你的项目应该有这些结果文件:
data/videos.csv:热点视频信息表,包含标题、播放量、弹幕数等。data/videos.json:原始数据备份。data/danmaku.csv:弹幕原始文本。output/cuts/:三个切片视频文件。
这三个产物分别对应数据采集、文本分析、视频处理三个能力点。即使以后不做速通切片内容,这套流程也可以应用到其他任何热点关键词上,换一个关键词就是一次新的分析任务。
5. 常见问题与排查思路
在实际运行过程中,最容易遇到的问题集中在接口请求和视频切片两部分。下面用表格整理高频问题和解决思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 搜索接口返回 code=-412 | 请求频率过高,或缺少 Referer 头 | 增加间隔时间,补全请求头,必要时添加浏览器 Cookie |
| 搜索结果为空列表 | 接口参数变更或关键词过于冷门 | 打开浏览器手动搜索确认关键词是否有结果,再检查返回 JSON 结构 |
| 获取 cid 失败 | bvid 已失效或视频被删除 | 跳过该视频,使用视频列表中的下一条数据 |
| 弹幕 XML 内容为空 | 视频关闭弹幕或 cid 过期 | 重新获取 cid,检查视频是否允许查看弹幕 |
| 切片输出黑屏 | 使用 -c copy 且切割点不在关键帧 |
改用重新编码方式,即 -c:v libx264 -c:a aac |
| FFmpeg 命令找不到 | ffmpeg.exe 不在系统 PATH | 在代码中指定 FFmpeg 完整路径 |
| CSV 用 Excel 打开中文乱码 | CSV 编码不是 UTF-8-SIG | 保存时使用 encoding="utf-8-sig" |
| jieba 分词结果不理想 | 缺少领域词表 | 添加自定义词,例如 jieba.add_word("末影奶龙") |
还有一个容易被忽略的点:B站搜索接口返回的标题字段会嵌入高亮 HTML 标签。如果不清理,写进 CSV 后会出现大量 <em> 字符,影响后续使用。这个问题在写爬虫时非常普遍,处理方式就是代码里的 clean_title 函数。
在实际抓取过程中,如果遇到某个视频接口报错,不要直接终止整个脚本,建议使用 try-except 捕获异常并继续处理后面的数据,保证批处理任务的稳定性。
6. 最佳实践与工程建议
6.1 请求频率与合规性
调用 B站接口时,一定要控制请求频率。本文代码中每页之间间隔 1 秒,弹幕下载之间间隔 1 秒,这是比较稳妥的设置。如果你需要抓取大量数据,可以把间隔提高到 2 到 3 秒,并在代码中加入随机等待时间,例如:
python复制import random
import time
time.sleep(1 + random.random())
这样可以让请求时间更分散,降低被风控识别为机器访问的概率。另外,使用爬虫获取的数据仅限个人学习和分析,不能用于商业盈利或大规模传播,这是基本的合规要求。
6.2 数据落盘与断点续采
爬虫在长时间运行中,难免遇到网络中断或接口异常。建议抓取每一条数据后立即追加写入 CSV,而不是等全部抓完再一次性保存。这样即使脚本中途崩溃,已抓取的数据也不会丢失。
可以在循环内每处理一条视频就调用 df.to_csv(..., mode="a", header=False) 追加写入。不过频繁写入效率较低,更合理的做法是在内存中累积一定数量后批量写入,比如每 10 条写一次。
6.3 代码模块化设计
三个模块之间尽量保持独立,不要在 bili_search.py 中直接调用 video_cut.py 的逻辑。每个脚本只做一件事,并对外提供函数接口。这样以后可以单独复用,也可以写一个新的 pipeline.py 把三个模块串起来。
模块化还有一个好处:任何一个接口字段改变时,只需要修改对应文件,其他代码不受影响。对于接口不稳定的场景,这种设计能显著降低维护成本。
6.4 FFmpeg 命名规范
批量切片时,输出文件名不要使用无意义的时间戳。建议按“原视频名_开始时间_时长.mp4”的规则命名,例如 speedrun_0123_10s.mp4。这样以后回看时能直接知道每个片段来自哪个原文件的哪个时间段。
甚至可以写一个函数自动从任务字典中生成文件名:
python复制def build_output_name(src_name: str, start: str, duration: str) -> str:
start_str = start.replace(":", "")
duration_str = duration.replace(":", "")
return f"{src_name}_{start_str}_{duration_str}.mp4"
命名规范看起来是小问题,但数据量变大之后,它决定了你的工作流是否高效。
6.5 异常处理与日志记录
生产环境运行脚本时,不要只用 print 输出日志。建议使用 Python logging 模块,输出到文件和控制台双重记录。这样脚本在后台跑一夜后,第二天可以查看日志文件定位问题。
更稳妥的方式是在主流程中增加异常捕获和失败重试机制。例如弹幕下载失败后,可以重试 3 次,每次等待 2 秒,仍失败才记录并跳过。
7. 总结与下一步学习路线
通过“末影奶龙速通切片”这个案例,我们完整走了一遍热点视频数据分析与切片处理流程。你掌握了 B站公开接口的基础调用、弹幕数据抓取与分词统计、FFmpeg 视频片段截取这三个核心技能,也了解了请求频率控制、数据保存、异常处理这些爬虫工程中的通用规范。
如果要把这个项目继续深入,可以从三个方向扩展:
一是增加可视化分析。用 matplotlib 绘制播放量对比图或弹幕热词词云,让分析结果更直观。词云生成代码并不复杂,核心是把 Counter 结果传给 WordCloud 对象。
二是接入定时任务。把搜索、弹幕分析、热点生成写成一套完整的定时脚本,每天固定时间自动运行一次,形成属于自己的热点监控系统。可以把脚本注册为 Windows 计划任务或者 Linux crontab 任务。
三是扩展视频处理能力。FFmpeg 不仅能切片,还能拼接、转码、添加字幕、抽取音频。你可以把多个切片片段按顺序拼接成一个合集视频,这就是很多速通搞笑合集视频的基本制作思路。拼接命令如下:
bash复制ffmpeg -f concat -i filelist.txt -c copy output.mp4
其中 filelist.txt 是待拼接文件的列表,每行格式为 file 'cut_01.mp4'。
本文示例基于当前公开接口和工具版本编写,如果你运行的环境与这里不完全一致,需要根据实际情况微调参数。技术和接口都在持续变化,但解决问题的思路是通用的。
如果你也喜欢研究这类热点数据,不妨打开电脑,把 bili_search.py 的关键词改成你感兴趣的内容,跑一遍完整流程。代码跑通之后,你会发现原本只是一个“图一乐”的网络热点,也可以变成一份有意思的技术练手项目。希望这篇教程能帮你打开思路,也欢迎收藏备用,方便以后实际动手时查阅。
