FEATURED · 精选文章

用Python和ffprobe实现录屏文件自动归档与批量重命名

发布时间 / 2026/9/4 3:04:55
来源 / 创域科博编辑部
栏目 / 资讯中心
用Python和ffprobe实现录屏文件自动归档与批量重命名 录屏文件一旦积累到几十个甚至上百个问题就变得很具体文件名里的“2026年8月14日”“小羊n”“19-20”“排档”到底算日期、对象、时段还是用途如果只是靠手动整理一旦发生误命名、重复覆盖、目录混乱找回原视频的成本会非常高。这篇文章以2026年8月14日小羊n 19-20排档录屏.mp4这类真实感很强的文件名为起点介绍一套可复现的录屏归档方法用 Python 解析文件名中的日期、时段和业务标签结合 ffprobe 读取视频元数据批量完成重命名、目录归档和校验。文章会给出最小可运行脚本也适合想用代码管理本地媒体资料的开发者参考。1. 录屏文件管理的核心问题不是没有信息而是信息无法被程序理解1.1 文件名里有信息但格式是给人看的不是给脚本用的2026年8月14日小羊n 19-20排档录屏.mp4是一个比较典型的录屏文件名。人眼能识别出几个信息单元日期是 2026 年 8 月 14 日“小羊n”可能代表录制对象或账号“19-20”可能代表某个时间档期“排档”可能是节目类型或板块名称最后是“录屏”。但这些信息对批处理程序并不友好日期使用中文年月日而不是YYYY-MM-DD按文件名排序时会得到错误的先后顺序。“19-20”作为字符串无法直接参与时间计算。“小羊n”中的n如果代表集数或编号没有统一前缀机器无法判断它到底是编号还是普通文本。各个信息之间只用空格分隔空格数量和位置不固定正则表达式很容易误判。如果把所有录屏都堆在同一个目录里这种文件名只能靠“打开预览”确认内容效率非常低。1.2 手工整理录屏的几种典型失败方式很多人会尝试手工整理常见做法是复制到新目录并手动改名。看起来直接但问题不少。第一种是“新建文件夹 手动改名”。目录名称和文件名完全凭记忆当初为什么叫小羊n过几个月再看就可能想不起来。第二种是“用 Excel 登记”。手动记录文件名、路径、备注刚开始能用一旦文件移动或重命名表格同步不及时信息就会失真。第三种是“只改文件标题不改系统元数据”。视频文件的标题、创建时间、录制时间都写在元数据里改外壳文件名并不会自动更新这些信息后续检索媒体库时依然无法归类。手工整理真正困难的不是“整理一次”而是“持续整理”。每次录完都有一堆新文件进来每次都要手动决定放哪个目录、叫什么名字出错的概率会随文件数量快速上升。1.3 自动化整理应该达到什么效果自动化不是为了让你完全不管文件而是为了把重复、机械、容易出错的操作交给脚本把决定权留给人。一次完整的自动化整理至少应该做到三点先解析后执行脚本先读取文件名和元数据生成一份可读的计划清单人确认后再执行。先模拟后移动先开启dry_run模式只输出“将要做什么”不实际修改文件。保留回滚线索每次重命名和移动的对应关系都要写入 CSV 或日志出错时能根据记录还原。这里的技术主线是用 Python 处理文件和目录用 ffprobe 读取视频元数据作为补充依据把所有录屏统一归档到archive/年份/月份/目录下形成稳定可检索的文件结构。2. 准备环境并让文件列表变成可处理的数据2.1 需要的基础工具与版本要求本文示例以 Python 3 和 FFmpeg 工具集中的 ffprobe 为基础。Python 3.8 以上即可因为代码中使用了pathlib它在 3.4 以后已经内置不依赖第三方库。这样可以减少环境变量折腾适合作为批处理脚本的基础。FFprobe 是 FFmpeg 自带的媒体探测工具能读取视频文件的分辨率、时长、编码格式、创建时间等信息。它不参与解码转码只读取头部信息速度很快。检查 Python 是否可用python --version检查 ffprobe 是否可用ffprobe -version在 Windows 上如果命令找不到需要把 FFmpeg 的bin目录加入PATH或者在脚本里直接写 ffprobe 的完整路径。学习环境可以把原始录屏放在一个独立的测试目录里例如D:/record/raw整理结果输出到D:/record/archive。不要一开始就对真正的素材执行脚本。2.2 建立实验目录结构建议在项目目录下创建两个目录raw存放待整理的原始录屏文件。archive存放整理后的结果。同时准备一个副本文件。把原始的2026年8月14日小羊n 19-20排档录屏.mp4复制一份到raw下作为测试对象。复制而不是直接操作原件是避免脚本误处理后无法恢复。目录结构大致如下record/ ├─ raw/ │ └─ 2026年8月14日小羊n 19-20排档录屏.mp4 ├─ archive/ └─ scripts/这里把脚本放在scripts目录下与原始素材分离避免脚本把.py文件也当作视频处理。2.3 用 Python 读取全部录屏文件先实现一个最基础的功能扫描raw目录下的所有 mp4 文件并打印文件名和大小。它能验证脚本运行环境是否正常。from pathlib import Path raw_dir Path(./raw) for video in sorted(raw_dir.glob(*.mp4)): size video.stat().st_size print(f{video.name} - {size} bytes)这段代码用Path.glob(*.mp4)查找扩展名为 mp4 的文件。Path.stat().st_size返回文件字节大小。运行后预期看到2026年8月14日小羊n 19-20排档录屏.mp4 - 328489321 bytes这证明 Python 能正确扫描中文文件名。如果在 Windows 命令行中出现乱码通常是终端编码问题需要把终端代码页切换为 UTF-8或者在脚本输出时使用 ASCII 安全内容。脚本内部对Path的操作不受影响。2.4 用 ffprobe 读取视频元数据文件名可能缺失日期但视频文件本身可能带有创建时间、时长和编码信息。ffprobe 可以弥补文件名的不足。先看一条命令ffprobe -v error \ -show_entries formatduration,size:streamcodec_name,width,height \ -of json raw/2026年8月14日小羊n 19-20排档录屏.mp4输出结果类似{ format: { duration: 3645.320000, size: 328489321, filename: raw/2026年8月14日小羊n 19-20排档录屏.mp4, nb_streams: 2 }, streams: [ { codec_name: h264, width: 1920, height: 1080 } ] }时长 3645 秒约等于 60 分钟与文件名里的“19-20”这个档期长度大致对应。这个信息可以帮助脚本校验比如当文件名写了19-20但视频实际时长只有 5 分钟就说明文件名可能标错。下面的脚本会把 ffprobe 嵌入 Python获取文件的时长和宽高import json import subprocess from pathlib import Path def probe_video(path: Path) - dict: cmd [ ffprobe, -v, error, -show_entries, formatduration,size:streamcodec_name,width,height, -of, json, str(path) ] result subprocess.run(cmd, capture_outputTrue, textTrue, encodingutf-8) if result.returncode ! 0: return {} return json.loads(result.stdout)在 Python 中使用subprocess调用外部程序时注意把textTrue和encodingutf-8写好否则中文路径在部分环境下会报编码错误。3. 解析文件名先拆日期、时段再保留业务标签3.1 推荐的目标命名格式在写解析规则前先确定整理后的目标格式。经过解析和归档后最好形成这样的结构archive/2026/08/2026-08-14_小羊n_1900-2000_排档.mp4这段路径可以拆成几层含义2026/08按年份和月份归档。2026-08-14标准化日期方便排序。小羊n业务主体保留原信息。1900-2000开始和结束时间补零成四位更利于脚本处理。排档业务标签或节目类型。这套格式不是唯一答案但它解决了几个典型问题日期可以从字符串排序时段可以精确表达业务标签和主体之间用下划线切分避免空格造成的解析歧义。信息原始写法目标写法变化原因日期2026年8月14日2026-08-14便于字典序排序开始时间191900补零后可排序结束时间202000与开始时间统一分隔符空格下划线避免正则误判3.2 用正则表达式提取信息基于上述目标格式写一个解析函数。下面是针对这个具体文件名的完整示例import re from pathlib import Path def parse_record_file(filename: str) - dict: stem Path(filename).stem # 1. 提取日期 date_pattern re.compile(r(\d{4})年(\d{1,2})月(\d{1,2})日) date_match date_pattern.search(stem) if date_match: year, month, day map(int, date_match.groups()) date_value f{year:04d}-{month:02d}-{day:02d} else: date_value None # 2. 提取时间段例如 19-20 time_pattern re.compile(r(\d{1,2})[-~至](\d{1,2})) time_match time_pattern.search(stem) if time_match: start_hour, end_hour map(int, time_match.groups()) start_value f{start_hour:02d}:00 end_value f{end_hour:02d}:00 else: start_value None end_value None # 3. 去掉日期和时段后剩余部分作为业务标签 cleaned_stem date_pattern.sub(, stem) cleaned_stem time_pattern.sub(, cleaned_stem) cleaned_stem re.sub(r\s, , cleaned_stem).strip() cleaned_stem re.sub(r[_-]$, , cleaned_stem) cleaned_stem re.sub(r录屏$, , cleaned_stem).strip() return { date: date_value, start: start_value, end: end_value, business: cleaned_stem or 未命名, } if __name__ __main__: sample 2026年8月14日小羊n 19-20排档录屏.mp4 print(parse_record_file(sample))运行后输出{date: 2026-08-14, start: 19:00, end: 20:00, business: 小羊n 排档}这里有几个细节需要注意日期正则用\d{4}限定四位年份避免误匹配文件里的其他数字。时间段正则兼容中文“至”、短横线“-”和波浪线“~”。业务标签的清洗不能过于暴力。例如文件名可能出现“录屏讲解”如果直接re.sub(录屏$, , ...)只删除末尾的“录屏”就比用替换全部更安全。3.3 文件名缺少日期时的回退策略真实场景不会每个文件都完美命名。比如文件名可能只是小羊n 19-20排档.mp4没有日期。这时可以回退到两个来源文件修改时间最后修改时间接近录制完成时间。ffprobe 读取的creation_time部分录制工具写入封装元数据。使用文件修改时间做回退最简单from datetime import datetime from pathlib import Path path Path(raw/小羊n 19-20排档.mp4) mtime datetime.fromtimestamp(path.stat().st_mtime) date_fallback mtime.strftime(%Y-%m-%d) print(date_fallback)但回退要谨慎。文件修改时间可能因为复制、下载而改变。更好的做法是一条告警规则如果文件名中没有日期脚本不自动使用修改时间而是把记录写入 CSV 的“建议日期”列由人工确认后再执行。学习环境下可以使用修改时间作为兜底生产环境则必须保证文件名中有可靠日期或在录制工具中开启自动时间戳命名。3.4 执行前先输出 CSV 清单批量整理最有价值的一步不是移动文件而是让计划结果可见。解析完成后先把每个文件的旧路径、新路径、日期、业务字段写入一个rename_plan.csv。import csv rows [ { old: raw/2026年8月14日小羊n 19-20排档录屏.mp4, new: archive/2026/08/2026-08-14_小羊n_1900-2000_排档.mp4, date: 2026-08-14, business: 小羊n 排档, } ] with open(rename_plan.csv, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnames[old, new, date, business]) writer.writeheader() writer.writerows(rows)用utf-8-sig编码写入是为了让 Windows 上的 Excel 直接打开 CSV 时能正确显示中文而不是乱码。检查完 CSV 后再执行实际文件操作能极大降低误操作风险。4. 批量重命名与归档脚本安全地移动真实文件4.1 核心执行策略先复制再移动后校验文件移动本身并不复杂真正复杂的是冲突和中断处理。如果直接执行shutil.move遇到目标目录不存在会报错遇到目标文件同名会覆盖造成不可逆数据丢失。因此脚本需要做到自动创建目标目录。重名时对比大小和修改时间。如果确实不同追加_01、_02序号而不是覆盖。先启用dry_run模式默认不移动任何文件。下面这段代码把前面解析逻辑组合成一个可运行的归档脚本。它假设当前目录结构是scripts、raw、archive三个兄弟目录实际使用时可调整路径。import csv import re import shutil import sys from datetime import datetime from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent SOURCE_DIR BASE_DIR / raw TARGET_DIR BASE_DIR / archive CSV_PATH BASE_DIR / rename_plan.csv def parse_record_file(filename: str) - dict: stem Path(filename).stem date_pattern re.compile(r(\d{4})年(\d{1,2})月(\d{1,2})日) time_pattern re.compile(r(\d{1,2})[-~至](\d{1,2})) date_match date_pattern.search(stem) if date_match: year, month, day map(int, date_match.groups()) date_value f{year:04d}-{month:02d}-{day:02d} else: date_value None time_match time_pattern.search(stem) if time_match: start_hour, end_hour map(int, time_match.groups()) start_value f{start_hour:02d}:00 end_value f{end_hour:02d}:00 else: start_value None end_value None cleaned_stem date_pattern.sub(, stem) cleaned_stem time_pattern.sub(, cleaned_stem) cleaned_stem re.sub(r\s, , cleaned_stem).strip() cleaned_stem re.sub(r[_-]$, , cleaned_stem) cleaned_stem re.sub(r录屏$, , cleaned_stem).strip() return { date: date_value, start: start_value, end: end_value, business: cleaned_stem or 未命名, } def build_target_path(parsed: dict, source_path: Path) - Path: if parsed[date]: date_value parsed[date] else: mtime datetime.fromtimestamp(source_path.stat().st_mtime) date_value mtime.strftime(%Y-%m-%d) year, month, _day date_value.split(-) name_parts [date_value] if parsed[business]: name_parts.append(parsed[business]) if parsed[start] and parsed[end]: start_compact parsed[start].replace(:, ) end_compact parsed[end].replace(:, ) name_parts.append(f{start_compact}-{end_compact}) safe_name _.join(name_parts) safe_name safe_name.replace(/, _).replace(\\, _).replace(:, ) return TARGET_DIR / year / month / f{safe_name}{source_path.suffix.lower()} def unique_path(path: Path) - Path: if not path.exists(): return path stem path.stem suffix path.suffix index 1 while True: candidate path.with_name(f{stem}_{index:02d}{suffix}) if not candidate.exists(): return candidate index 1 def run(dry_run: bool True): rows [] source_files sorted(SOURCE_DIR.glob(*.mp4)) if not source_files: print(No mp4 files found in raw directory.) return for source_path in source_files: parsed parse_record_file(source_path.name) target_path build_target_path(parsed, source_path) target_path unique_path(target_path) rows.append({ old: str(source_path), new: str(target_path), date: parsed[date] or , business: parsed[business], }) if not dry_run: try: target_path.parent.mkdir(parentsTrue, exist_okTrue) shutil.move(str(source_path), str(target_path)) except OSError as exc: print(fFailed: {source_path.name} - {exc}) with open(CSV_PATH, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnames[old, new, date, business]) writer.writeheader() writer.writerows(rows) print(fdry_run{dry_run}, files{len(rows)}) print(fplan written to {CSV_PATH}) if __name__ __main__: dry_run --apply not in sys.argv run(dry_rundry_run)脚本执行逻辑是默认空跑执行python rename_records.py。检查rename_plan.csv确认无误后执行python rename_records.py --apply才会真正移动文件。4.2 处理文件重名冲突目标目录中很可能已经存在同名文件。unique_path函数会在目标文件名后加_01、_02而不是直接覆盖。例如2026-08-14_小羊n_1900-2000_排档.mp4已存在脚本会自动生成2026-08-14_小羊n_1900-2000_排档_01.mp4。这种做法保留了同名内容但会破坏“一文件一位置”的整洁度。更稳妥的做法是在第一次生成 CSV 时就检查并列出重名文件由人判断是重复文件、补录文件还是原始命名错误。4.3 移动前后要增加日志脚本输出了 PDF-like? 不是 CSV。但 CSV 只是计划不是日志。执行时还需要在控制台打印每一条实际变化print(fMOVE: {source_path.name}) print(f - {target_path.relative_to(BASE_DIR)})如果脚本在移动第 30 个文件时因为权限不足中断日志能告诉你前 29 个文件去了哪里第 30 个文件停在哪里便于继续而不重复处理。5. 运行验证与常见问题排查5.1 怎么确认脚本处理结果正确不要等执行完直接关终端。建议按下面顺序验证一遍。第一步在测试副本上运行空跑python rename_records.py第二步打开rename_plan.csv确认每个文件的old路径和new路径是否合理。预期数据类似old,new,date,business raw/2026年8月14日小羊n 19-20排档录屏.mp4,archive/2026/08/2026-08-14_小羊n_1900-2000_排档.mp4,2026-08-14,小羊n 排档第三步执行正式移动python rename_records.py --apply第四步再次扫描archive目录确认文件数量不缺失find archive -type f -name *.mp4 | wc -lWindows 上可以在 PowerShell 中使用(Get-ChildItem -Path archive -Filter *.mp4 -Recurse).Count把raw中剩余数量与archive中数量相加应该等于原始文件总数。这里的目标是“移动”而不是“复制”所以原始目录应该清空或明显减少。5.2 文件没有按预期解析时怎么办如果文件名格式和示例有差异脚本可能解析不到日期或者把业务标签识别成空字符串。需要先单独调试解析函数也就是只执行parse_record_file()不要立刻跑整个脚本。常见情况如下问题现象常见原因处理方式date为空文件名没有中文年月日改用 ffprobe 的creation_time或文件修改时间并人工确认start和end为空文件名只有“晚场”而无 19-20 这种数字时段不要自动猜在 CSV 中留空后续人工补充business包含“录屏”字样业务标签里本来就是“录屏”二字修改清理规则只在文件名末尾确实是“录屏.mp4”时删除只识别出日期后面全没识别正则表达式的空格假设不成立检查stem内容和空格数量增加通用分隔符处理Windows 下 CSV 中文乱码编码写成了utf-8Excel 用 GBK 打开写入时使用encodingutf-8-sig5.3 执行时报错和对应的检查步骤遇到报错时按下述顺序排查一般可以定位大部分问题。先确认文件名是否真的在raw目录里。检查路径大小写、目录层级、扩展名是不是.mp4。再看解析结果。打印parse_record_file的结果确认正则没有把关键字段吞掉。检查目标目录是否已经存在同名目录或文件。unique_path能避免覆盖但如果目标路径过长Windows 上会触发OSError。确认是否有软件占用文件。播放器、网盘客户端、杀毒软件都可能占用视频文件导致shutil.move抛出权限错误。典型报错示例PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: raw/xxx.mp4处理方式关闭正在预览该文件的播放器或剪辑软件再重新执行脚本。另一个常见报错是路径过长OSError: [WinError 206] The filename or extension is too long可以在 Windows 系统设置中启用长路径支持也可以把业务标签精简后再归档避免文件名越来越长。5.4 执行中断后如何续跑移动文件时如果中途中断最保险的做法是将已经成功移动的记录从新目录移回原目录或者先从 CSV 中筛选尚未处理的行再单独重新执行。如果数据量大不建议直接重新跑全量脚本因为已经移动的文件可能再次被扫描到。虽然unique_path会避免覆盖但可能产生重复副本。更合理的做法是分批移动每次只处理 50 或 100 个文件。修改run函数给它加一个limit参数def run(dry_run: bool True, limit: int 50): source_files sorted(SOURCE_DIR.glob(*.mp4))[:limit] ...这样即使出错影响面也小排查成本低。6. 从“文件归档”走向“视频素材管理”6.1 用媒体元数据校验文件名信息归档完成后还可以把 ffprobe 的结果写进 CSV让文件清单不只是“路径清单”而是“媒体资料库”。用一个循环读取每个新文件的信息再把时长和分辨率加入汇总表ffprobe -v error \ -show_entries formatduration:streamcodec_name,width,height \ -of csvp0 \ archive/2026/08/2026-08-14_小羊n_1900-2000_排档.mp4输出类似3600.000,0,h264,0,1920,1080这里的0是时间基字段不同 ffprobe 版本输出可能略有差异以实际结果为准。得到的数据可以追加到 CSV 中形成带时长的文件清单。文件名中的时间段“1900-2000”可以换算成小时数 1 小时再用 ffprobe 得到的真实时长做对比。如果真实时长只有 10 分钟说明文件可能是压缩版、截断版或文件名标错。6.2 使用 ffmpeg 生成预览缩略图录屏文件的检索难点是“看不出内容”。即使文件名规范你也不能只靠文件名记住整个视频里讲了什么。生成缩略图是一个低成本的辅助方案。从视频第 300 秒截取一帧作为预览图ffmpeg -ss 300 -i archive/2026/08/2026-08-14_小羊n_1900-2000_排档.mp4 \ -frames:v 1 -q:v 4 \ archive/2026/08/2026-08-14_小羊n_1900-2000_排档.jpg-ss 300表示从第 300 秒开始-frames:v 1表示只输出一帧-q:v 4控制 JPEG 质量。生成缩略图后即使文件没有完整预览文件管理器也能直接看到视频中的画面节省不少时间。这里的“300 秒”只是一个示例实际项目中可以从视频的不同时间段抽取多张图组成一张“九宫格”预览图更接近视频封面。6.3 转码压缩前先确认源文件是否要保留录屏文件体积往往很大。使用 FFmpeg 压缩是常见需求但在归档链路中压缩不应是第一步。推荐顺序是先按规范命名并归档原始文件。把原始文件移动到“原始未压缩”目录或写入备份策略。再对副本进行压缩或转码。生成压缩文件后用 ffprobe 检查时长、分辨率、码率是否达标。删除原始文件前保留至少一个备份周期。压缩示例ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4-crf控制视频质量控制-preset控制编码速度与压缩率平衡。不同录屏素材适合的crf不同建议先压缩一个短片段对比画质和体积不要对整个文件直接使用参数。7. 录屏文件管理的可复用清单7.1 文件名命名的推荐规范如果条件允许最好的方案不是“整理旧文件”而是“从源头避免制造混乱”。可以从下一个录屏开始使用以下命名规范YYYY-MM-DD_项目名_主体标识_开始时间-结束时间_场景说明.mp4示例2026-08-14_直播录屏_小羊n_1900-2000_排档.mp4使用建议日期放在最前面天然可排序。统一用下划线分隔字段避免空格导致命令处理出错。时间使用 24 小时制并补零如1900而不是19。保留原始文件的扩展名小写。避免在文件名中出现/、\、:、*、?、、、、|等特殊字符Windows 文件系统无法容忍这些字符。7.2 自动化脚本上线前的检查清单执行任何批量移动脚本前建议逐项确认检查项说明是否完成使用测试目录不要直接在工作目录执行保留源文件备份原始副本至少保留一份开启dry_run空跑确认计划 CSV 没有明显异常检查 CSV 编码使用utf-8-sig方便 Excel 查看统计文件数量处理前后数量保持一致关闭文件占用程序避免播放器或剪辑软件锁定文件分批处理新脚本先处理 10 个文件验证保留执行日志记录每个文件从哪里移动到哪里准备回滚方案保留 CSV必要时反向移动7.3 三个适合继续深入的方向第一为不同来源的录屏开发“解析适配器”。不同主播、课程平台或摄像头录屏工具文件名规则可能完全不同。可以把解析函数拆成多个策略先用正则猜测再人工确认。第二建立视频指纹去重机制。重复下载和重复录制会占用磁盘空间。可以先通过文件大小初筛再用 FFmpeg 计算视频流哈希或感知哈希找出真正重复的文件。第三对接媒体管理工具。本地批量归档只是第一步。把整理后的文件纳入 Jellyfin、Kodi 等媒体库需要额外的封面抓取、剧集排序和 NFO 文件生成逻辑。如果能把这篇文章中的 CSV 清单和媒体库扫描逻辑打通录屏文件管理就可以变成一个小型资产管理工具。整理录屏的技术门槛并不高真正有价值的是“先计划、后执行、可回滚”的习惯。只要文件命名规范、解析规则稳定、每次操作都留日志即使视频数量再大也能在几分钟内完成归档和检索。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻