做短视频素材管理的朋友应该都遇到过这种噩梦硬盘里堆了几万条视频运营突然丢过来一句把三个月前创建的那批直播录像找出来准备清掉你打开文件夹一看根本没法用肉眼判断哪条视频是什么时候创建的更别提还要在Excel里一条条标出来哪些要删。我最近处理的这个857号需求就是典型的批量视频创建时间筛查任务——把指定区间内创建的视频全部提取出来在Excel里标注清楚哪些属于待删除文件。这篇文章就手把手拆解整个流程从文件时间戳的原理到Python脚本实现再到Excel标注和删除安全策略一次性讲透。这个方案不只适用于视频文件。凡是需要按创建时间筛选批量文件的场景不管是清理监控录像、整理课程切片、归档项目素材还是审核导出文件的时效性思路完全一样。1. 先想清楚这个需求背后真正要解决的是什么1.1 标题里的三段需求逐条拆解把857-批量视频创建时间在指定区间修改-提取创建时间并在excel中标注需要删除文件这个标题拆开看实际包含三个动作第一批量。这不是三五个文件而是成百上千甚至几万条视频。手动右键看属性再一条条登记在数量大时完全不现实。第二创建时间在指定区间。这是筛选条件比如2024-01-01到2024-03-31之间创建的文件时间里藏着业务决策的依据。第三提取创建时间并在Excel中标注需要删除的文件。这一步是交付物要让不懂技术的人也能打开Excel就能看懂哪些该删、哪些保留。仔细读这个标题会发现指定区间修改这个表述有点歧义。它可能是指修改创建时间为指定区间也可能是指在指定区间内修改/筛选文件。结合后半句提取创建时间并标注需要删除文件来看实际场景更接近后者——你并不需要真的去篡改文件时间而是要把落在指定时间区间内的视频挑出来标记为待删除。真正的批量修改创建时间是另一个完全不同的需求一般要用专门的工具改NTFS元数据不在这次讨论范围内。1.2 为什么不能靠手工硬扛我见过很多人面对这类需求的第一反应是打开文件夹排个序CTRLC复制文件列表到Excel再挨个查属性。小批量几十个文件确实能这样干但一旦超过500个文件这种操作就是灾难。手工操作至少有三个痛点一是效率极低每个文件要右键属性、切到详细信息、看创建时间一个文件十几秒1000个文件就是三个多小时二是容易出错漏看、填错、排错是常态尤其在文件名相似的时候三是无法留痕做完之后没有一份可复核的清单万一领导问这批删除的判断依据是什么你拿不出可回溯的证据。所以这个项目的核心价值不只是把活干完更是产出一份结构化的、可复核的数据清单。这份Excel本身就是交付物和管理依据后续审计、复盘都要靠它。1.3 技术路线怎么选Python、批处理脚本还是现成软件做这类批量文件筛查主流的路线有三条我直接给结论。第一条是Python脚本。这是最推荐的方式尤其是装上pandas和openpyxl之后遍历文件、提取时间、写Excel一条龙搞定。Python处理几千个文件的元数据库耗时基本在几秒到十几秒之间体验远优于手动操作。第二条是PowerShell或批处理。好处是Windows自带的PowerShell不需要装任何环境直接用Get-ChildItem加Export-Csv也能导出文件列表。缺点是代码可读性差时间筛选和Excel标注逻辑写起来不如Python顺手复杂条件一多就容易绕晕。第三条是现成的文件管理软件比如某些重复文件清理工具或者资源管理器增强插件。这类工具可视化好但灵活性差——你没法自定义创建时间区间Excel标注这种组合逻辑也很难把结果合并进自己的工作流。综合下来凡是想把这事做成可复用流程的我都建议走Python路线。哪怕你不太会写代码照着下面第三节的脚本抄一遍改几个路径参数就能跑通。2. 关键前置知识文件时间戳里藏着哪些坑2.1 创建时间、修改时间、访问时间先分清很多新手在提取创建时间的时候会踩坑因为操作系统里时间不只一个。Windows文件属性里能看到创建时间、修改时间、访问时间三个独立字段。创建时间文件在当前磁盘分区上被创建的时间。这是筛选哪批文件是哪个时间段产生的最直接的依据。修改时间文件内容最后一次被写入更改的时间。常用于判断文件是否被改动过。访问时间文件最后一次被打开读取的时间。这个值在不同系统下更新策略不同Windows对NTFS分区一般延迟更新所以可靠性相对差。用Python取时间戳时三个字段的接口分别是os.path.getctime(path) —— 创建时间对应Windows的CreationTimeos.path.getmtime(path) —— 修改时间对应LastWriteTimeos.path.getatime(path) —— 访问时间对应LastAccessTime请注意getctime在Linux和macOS上返回的是文件状态变更时间不是创建时间。这是跨平台使用时最容易误解的地方。如果你只在Windows上跑脚本那没问题但如果你在macOS上跑同样的代码结果是错的。所以我在实际项目中都会在脚本开头加个系统判断非Windows环境就提示用户改用修改时间逻辑。2.2 复制与移动会让创建时间失真这一条是实战中的大坑。同一个视频文件从相机存储卡复制到电脑硬盘再用U盘拷到另一台电脑创建时间很可能会变成复制行为发生的时间而不是拍摄时间。具体来说在Windows资源管理器里用CtrlC和CtrlV复制文件系统会给目标位置生成一个新的创建时间记录的是复制动作完成的那一刻。这意味着你可能明明筛的是三个月前拍的视频结果创建时间全是今天。怎么判断手里的视频时间可不可信几个经验值用读卡器直接把素材导入电脑创建时间基本等于拍摄时间前提是相机文件系统支持时间元数据通过网盘、微信、邮件下载的文件创建时间大概率是下载时间用FastCopy、Total Commander等支持保留时间戳的工具复制可以尽量保住原始时间。所以在我们项目开工前我先确认了这批视频是从监控设备直接导出来的没有二次复制过。如果你不确定自己的文件是否被复制过建议先小范围抽查几个文件右键对比属性里的创建时间和拍摄画面水印再决定用创建时间还是修改时间作为筛选依据。2.3 指定区间筛选的两种实现思路明确了时间语义之后筛选逻辑本身有两种做法。第一种是先提取全部时间再在Excel中按区间筛选。这种方式的好处是保留了全量数据Excel里你想切什么区间都行不用反复改脚本重新跑。第二种是在Python代码里写死时间区间只导出区间内的文件。好处是结果文件简洁但每次换区间都得改代码不够灵活。我的建议是先用第一种做一版全量清单把视频名、创建时间、修改时间、文件大小、完整路径全部导出。之后在Excel里用自动筛选或者条件格式来圈定任何时间段。这相当于一次采集终身复用。后续如果要交付固定格式的报告再写个轻量脚本从全量Excel里按条件抽数据即可。3. 整体方案设计与Python实现3.1 五步流程总览我实际跑通的流程分五步指定要扫描的视频文件夹路径支持递归遍历所有子目录用os.walk遍历目录筛选出视频扩展名.mp4、.mov、.avi、.mkv、.flv等对每个文件调用os.path.getctime提取创建时间转成可读的YYYY-MM-DD HH:MM:SS格式把全部信息写进pandas的DataFrame再导出到Excel同时自动生成是否删除的标注逻辑生成一份待删除清单Sheet并给行标色便于人工复核。这五步每一步都不复杂但组合起来就是一个完整的小工具。以后你手里再来新视频只要改一下路径参数重新跑一遍就能刷新清单。3.2 环境准备与依赖安装你需要一个Python 3.8以上的环境推荐3.10或3.11。如果电脑上还没装Python建议去官网下载安装包装的时候勾选Add Python to PATH省得后面在命令行找不到命令。装完Python之后打开命令行Windows是CMD或PowerShellmacOS/Linux是终端运行下面这行命令安装依赖pip install openpyxl send2trash这里openpyxl负责读写Excelsend2trash负责把文件删除到回收站而不是直接抹掉数据后面删除安全策略会用到。如果你习惯用pandas也可以一起装上pip install pandas不过说句实话这个场景用纯Python的标准库加openpyxl就够了pandas只是让代码更简洁。我下面给的示例会用pandas因为处理几千行的DataFrame和导出Excel确实方便你也更好理解。3.3 核心代码遍历目录并提取创建时间先上一段最核心的文件扫描代码这个脚本会遍历指定目录提取视频文件的各项时间信息并把结果打印出来。import os import time from datetime import datetime # 要扫描的文件夹改成你自己的路径 TARGET_DIR rD:\video_archive # 需要过滤的视频扩展名按需增删 VIDEO_EXTS {.mp4, .mov, .avi, .mkv, .flv, .wmv, .ts} records [] for root, dirs, files in os.walk(TARGET_DIR): for name in files: ext os.path.splitext(name)[1].lower() if ext not in VIDEO_EXTS: continue full_path os.path.join(root, name) try: # 获取创建时间戳Windows下是真正的创建时间 ctime os.path.getctime(full_path) # 获取修改时间戳 mtime os.path.getmtime(full_path) except OSError as e: # 部分文件可能权限异常或已被占用 print(f跳过无法访问的文件: {full_path}错误: {e}) continue # 把时间戳转成可读字符串 create_str datetime.fromtimestamp(ctime).strftime(%Y-%m-%d %H:%M:%S) modify_str datetime.fromtimestamp(mtime).strftime(%Y-%m-%d %H:%M:%S) file_size os.path.getsize(full_path) records.append({ 文件名: name, 完整路径: full_path, 创建时间: create_str, 修改时间: modify_str, 大小MB: round(file_size / (1024 * 1024), 2) }) print(f扫描完成共找到 {len(records)} 个视频文件。)这段代码有几个细节值得解释os.walk会递归遍历所有子目录所以不管你的视频是散落在根目录还是分门别类放在子文件夹里都能扫到。os.path.splitext(name)[1].lower()把扩展名转成小写再比对防止出现.MP4和.mp4混用导致漏筛的情况。每个文件的访问做了try/except兜底因为实际文件夹里总会遇到某些文件名格式怪癖或者被其他进程占用的文件不能让一个异常中断整个扫描。3.4 区间筛选与Excel标注完整示例扫描出全量信息之后接下来的重点就是筛选和标注。我直接把完整脚本写出来你复制后改两个参数就能用。import os from datetime import datetime from openpyxl import Workbook from openpyxl.styles import PatternFill from openpyxl.utils import get_column_letter # 配置区 TARGET_DIR rD:\video_archive # 你要关注的开始时间和结束时间格式必须严格按 YYYY-MM-DD HH:MM:SS START_TIME 2024-01-01 00:00:00 END_TIME 2024-12-31 23:59:59 EXCEL_PATH rD:\video_archive\视频清单.xlsx VIDEO_EXTS {.mp4, .mov, .avi, .mkv, .flv, .wmv, .ts} # def parse_time_str(time_str): return datetime.strptime(time_str, %Y-%m-%d %H:%M:%S) start_dt parse_time_str(START_TIME) end_dt parse_time_str(END_TIME) records [] for root, dirs, files in os.walk(TARGET_DIR): for name in files: ext os.path.splitext(name)[1].lower() if ext not in VIDEO_EXTS: continue full_path os.path.join(root, name) try: ctime os.path.getctime(full_path) mtime os.path.getmtime(full_path) except OSError: continue create_dt datetime.fromtimestamp(ctime) create_str create_dt.strftime(%Y-%m-%d %H:%M:%S) modify_str datetime.fromtimestamp(mtime).strftime(%Y-%m-%d %H:%M:%S) size_mb round(os.path.getsize(full_path) / (1024 * 1024), 2) # 判断是否落在指定区间 in_range start_dt create_dt end_dt records.append({ 文件名: name, 创建时间: create_str, 修改时间: modify_str, 大小MB: size_mb, 完整路径: full_path, 是否在时间区间内: 是 if in_range else 否, 是否建议删除: 是 if in_range else 否, }) # 按创建时间排序 records.sort(keylambda x: x[创建时间]) # 写入Excel wb Workbook() ws wb.active ws.title 全部视频清单 headers [文件名, 创建时间, 修改时间, 大小MB, 完整路径, 是否在时间区间内, 是否建议删除] ws.append(headers) # 表头加粗 from openpyxl.styles import Font header_font Font(boldTrue) for col in range(1, len(headers) 1): ws.cell(row1, columncol).font header_font # 待删除标记行用黄色填充 yellow_fill PatternFill(start_colorFFFF00, end_colorFFFF00, fill_typesolid) for record in records: row_idx ws.max_row 1 ws.append([ record[文件名], record[创建时间], record[修改时间], record[大小MB], record[完整路径], record[是否在时间区间内], record[是否建议删除], ]) # 如果在区间内整行标黄 if record[是否建议删除] 是: for col in range(1, len(headers) 1): ws.cell(rowrow_idx, columncol).fill yellow_fill # 生成单独的待删除清单Sheet ws2 wb.create_sheet(title待删除清单) ws2.append(headers) for col in range(1, len(headers) 1): ws2.cell(row1, columncol).font header_font for record in records: if record[是否建议删除] 是: ws2.append([ record[文件名], record[创建时间], record[修改时间], record[大小MB], record[完整路径], record[是否在时间区间内], record[是否建议删除], ]) # 调整列宽避免文件名或路径显示不全 col_widths [30, 20, 20, 12, 50, 18, 18] for i, width in enumerate(col_widths, start1): ws.column_dimensions[get_column_letter(i)].width width ws2.column_dimensions[get_column_letter(i)].width width wb.save(EXCEL_PATH) print(f完成Excel已保存到 {EXCEL_PATH}) print(f视频总数: {len(records)}) print(f区间内建议删除: {sum(1 for r in records if r[是否建议删除] 是)})运行完这个脚本你会得到一个包含两个Sheet的Excel文件。第一个Sheet是全量视频清单区间内的行已经被自动填充了黄色第二个Sheet是干净利落的待删除清单只保留需要处理的项目。3.5 关键逻辑解释为什么整行标黄而不是加一列我在做Excel标注的时候选的是整行标黄加单独清单Sheet的双保险策略。你可能会问既然已经在是否建议删除列写了是为什么还要费劲去填充背景色因为人眼对颜色的敏感度远高于对文字的敏感度。几千条数据摆在那里你让操作人员一条条看文字判断是是还是否他们很容易疲劳出错。整行标黄之后待删除项在视觉上被自动分组往下一扫就能锁定区域。另外单独生成一个待删除清单Sheet也不是冗余。实际交付的时候操作人员只需要打开这个Sheet看到的全部都是要处理的视频。不需要在几千行里面筛选来筛选去也不需要懂Excel筛选功能。这大大降低了使用门槛对方只需要按图索骥即可。还有个小细节我把列宽设置了固定值文件名和完整路径两列给了足够的宽度。因为Excel默认列宽在遇到超长路径时会显示成######打印和查看都困难。这个问题在真实项目里经常被人忽略等到交付的时候才发现表格没法看。4. 实际运行中的常见问题与排查技巧4.1 扫描慢、内存占用高怎么办第一次跑这个脚本的时候如果视频文件夹里有几万条文件你可能会发现程序要跑好几分钟。这通常不是代码逻辑问题而是文件数量导致磁盘I/O和元数据读取密集。几个优化技巧如果目录层级极深且文件数极多可以在不需要全量清单的情况下把扫描范围缩小到特定的子文件夹。在TARGET_DIR上直接指向更精确的路径即可。如果确认所有视频都在第一层目录里可以把os.walk换成os.listdir只扫当前层。速度提升非常大但代价是无法覆盖子目录。开一个进度日志每处理1000个文件打印一次耗时和当前进度。这样跑批任务的时候心里不慌也能判断是否有文件导致卡死。常规量级的上千个文件其实用Python扫反而很快真正耗时大户是把Excel写盘的过程。如果数据量在5万行以上建议把openpyxl换成pandas的to_excel直接输出速度能快一个数量级。4.2 Excel文件被占用导致保存失败这是使用openpyxl时最经典的坑你运行脚本脚本执行到wb.save(EXCEL_PATH)时报PermissionError原因几乎一定是目标Excel文件正被打开着。Windows默认禁止对正在打开的文件进行写入操作。解决办法有两个第一个最简单保存前确保Excel完全关闭包括关闭后台预览窗口第二个更稳妥脚本里加一个异常捕获如果保存失败就自动换一个带时间戳的文件名重新保存。这样即使Excel文件被占用脚本也能完成数据输出不会中断任务。实现方式是这样import datetime try: wb.save(EXCEL_PATH) except PermissionError: alt_path EXCEL_PATH.replace(.xlsx, f_backup_{datetime.datetime.now().strftime(%Y%m%d_%H%M%S)}.xlsx) wb.save(alt_path) print(f原文件被占用已保存到: {alt_path})这样处理后哪怕用户忘记关Excel脚本也不会白跑一趟。4.3 按天、按时段精确筛选的参数写法很多实际需求不是从2024-01-01 00:00:00到2024-12-31 23:59:59这么整年的区间而是比如3月20号凌晨2点到3月21号凌晨1点这样的精确时段。这时候你就需要理解时间字符串格式的拼接规则。脚本里我用的是%Y-%m-%d %H:%M:%S格式24小时制。如果你的时间跨度是某一天直接写成2024-03-20 00:00:00到2024-03-20 23:59:59即可。有读者可能会问只写日期不带时间行不行比如START_TIME写成2024-03-20。答案是不行因为strptime解析时要求字符串格式必须和模板完全匹配。如果你确实想省事可以把parse_time_str改成def parse_time_str(time_str): for fmt in (%Y-%m-%d %H:%M:%S, %Y-%m-%d): try: return datetime.strptime(time_str, fmt) except ValueError: continue raise ValueError(f无法解析的时间格式: {time_str})这样2024-03-20会自动补成当天的2024-03-20 00:00:002024-03-20 23:59:59则按完整格式解析。注意2024-03-20作为结束时间时缺省补的是零点所以如果你希望包含整个20号结束时间要写成2024-03-21或2024-03-20 23:59:59这个逻辑要想清楚否则会漏掉当天的尾部数据。4.4 常见报错与解决速查表我把实际运行中比较高频的问题整理成了表格方便你对照排查。问题现象可能原因解决办法扫描结果为0个文件扩展名大小写问题或路径不存在确认TARGET_DIR路径存在检查扩展名是否在VIDEO_EXTS里比如.MP4需要写成.mp4也行代码里已经做了小写转换时间全是1970年或者固定值在Linux或macOS上跑os.path.getctime或者文件系统不支持创建时间确认系统类型改用os.path.getmtime获取修改时间PermissionError保存失败目标Excel文件被打开关闭Excel后重跑或用上面讲到的自动备用文件名方案文件路径包含中文乱码控制台编码问题在脚本开头加import sys; sys.stdout.reconfigure(encodingutf-8)或输出路径时直接写入Excel不做print文件太多导致内存不足几万条数据全放内存的records列表改用sqlite3暂存数据或分批处理每处理5000条写入一次临时文件删除视频后重新运行Excel仍有旧记录脚本重新扫描时没有清空原来的输出文件脚本每次运行前用os.remove删除旧的EXCEL_PATH再重新生成保证结果新鲜5. 从标注到删除这一环节千万别图省事5.1 我为什么不建议脚本直接删除文件文章标题里有需要删除文件这几个字但整个自动化流程跑到生成Excel标注就足够了。我不建议把删除动作直接写进脚本里更不建议在标注完的瞬间就执行删除操作。原因很简单文件删除是一个不可逆的高风险操作。尤其是视频素材一旦删错可能意味着几个T的项目文件、原始素材、未归档的创作纪录全部丢失。哪怕你有回收站兜底大量文件同时进回收站也会导致后续恢复困难。我在实际项目里的做法是脚本只负责标记和生成清单删除动作全部由人工在确认清单之后执行。而且即使用脚本执行删除也坚决不直接用os.remove而是用send2trash.send2trash()这个函数会把文件放进系统回收站给后续误删留一条后悔药。5.2 合理的复核流程怎么设计一份好的交付流程应该是这样的跑脚本生成Excel区间内的视频被标黄并汇总到待删除清单Sheet人工在Excel里抽查待删除清单中的文件重点验证创建时间是否可信有没有可能被误删的近期剪辑素材将待删除清单里的文件移动到专门设置的to_delete临时文件夹而不是立即删除临时文件夹保留至少一到两周确认业务方无异议后再清空回收站。这个两步走的策略在团队协作中尤其管用。你硬要说这些视频已经标了删除直接删了吧业务方一定会心虚但你先移动到一个临时隔离文件夹再给业务方一个恢复窗口对方的接受度就会高很多。5.3 从筛选到分档更大的扩展方向这次做的是指定区间创建时间是否删除的二元标注但类似逻辑完全可以扩展成更复杂的分档策略。比如按视频时长分档用ffprobe读取视频时长把超过10分钟的标记为长视频待压缩小于30秒的标记为废弃片段待清理按文件大小分档超过500MB的标为大文件需转码低于50MB的标为可能不完整按路径分档根目录散落的临时文件标为待归档特定项目文件夹内的标为保留。这些本质上都是元数据提取 条件判断 Excel标注的排列组合。你这次掌握了创建时间的玩法后面要加分辨率、时长、码率、拍摄机型等信息只要对应调用专业工具取元数据流程完全复用。我个人在实际操作中的体会是这类批量文件管理任务最容踩的坑不是代码写不出来而是没想清楚我要以哪个时间字段为准删除动作的安全边界在哪里就开始写代码。先把业务逻辑定死把复核路径留好脚本只是把脏活累活自动化而已。最后再分享一个实际操作中的小技巧生成Excel后顺手在表格最上方加一行说明写上筛选的时间区间和生成时间。比如本清单由自动脚本生成筛选区间2024-01-01至2024-12-31生成时间2025-01-15 20:11:23。这份Excel如果传到别人手里对方可以立即理解数据口径避免反复追问。等以后文件数量上百G、面临磁盘空间告急的时候你翻到这份清单也能快速回想出当时的决策背景。这批文件的最终归属在Excel里分析并通过脚本把存疑内容标黄之后其实只差最后的人工点头。