Python文件夹遍历案例:如何高效扫描文件?从入门到实战
目录导读
为什么需要文件夹遍历?
在日常开发或运维工作中,我们经常需要批量处理文件。

- 清理临时文件(.log、.tmp)
- 查找大文件或重复文件
- 批量重命名或转移文件
- 构建文件索引或备份列表
核心问题:如何用Python高效、灵活地遍历文件夹及其子文件夹,提取目标文件?
Python标准库提供了os、os.path和pathlib(Python 3.4+)等模块,本文将结合搜索引擎已有文章精华,从基础到高级,为您梳理一套可直接用于生产环境的解决方案。
Python遍历文件夹的三种核心方法
方法1:os.listdir() + 递归(基础版)
import os
def scan_files_by_os(path):
result = []
for item in os.listdir(path):
full_path = os.path.join(path, item)
if os.path.isfile(full_path):
result.append(full_path)
elif os.path.isdir(full_path):
result.extend(scan_files_by_os(full_path)) # 递归
return result
优点:无额外依赖,适合简单目录。
缺点:需要手动处理递归,性能中等。
方法2:os.walk()(生产首选)
os.walk() 自动递归遍历目录树,返回三元组(root, dirs, files)。
import os
def scan_files_by_walk(root_dir):
file_list = []
for root, dirs, files in os.walk(root_dir):
for file in files:
file_list.append(os.path.join(root, file))
return file_list
优点:
- 代码简洁,无需手动递归
- 支持深度控制(可通过修改
dirs列表实现剪枝) - 性能优于手动递归(内部用C实现)
方法3:pathlib.Path.rglob()(现代Python推荐)
from pathlib import Path
def scan_files_by_pathlib(root_dir):
return [str(p) for p in Path(root_dir).rglob('*') if p.is_file()]
优点:
- 面向对象,API更直观
- 支持通配符(如
rglob('*.txt')直接过滤) - 与
os模块相比,路径处理更安全
选择建议:
- 简单脚本:
os.walk()或pathlib - 需要过滤文件类型:
pathlib.rglob('*.py') - 需要处理符号链接或特殊权限:
os.walk()配合os.access()
实战案例:扫描指定类型文件并生成报告
假设我们需要扫描/data/project下所有.txt和.log文件,并输出每个文件的绝对路径、大小、修改时间。
完整代码(带异常处理)
import os
from datetime import datetime
from pathlib import Path
def scan_with_report(root_dir, extensions=('.txt', '.log')):
report = []
errors = []
for root, dirs, files in os.walk(root_dir):
# 可选:跳过隐藏目录
dirs[:] = [d for d in dirs if not d.startswith('.')]
for file in files:
if file.endswith(extensions):
full_path = os.path.join(root, file)
try:
stat = os.stat(full_path)
report.append({
'path': full_path,
'size_kb': round(stat.st_size / 1024, 2),
'modified': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
})
except PermissionError:
errors.append(f'权限不足: {full_path}')
except FileNotFoundError:
errors.append(f'文件已被删除: {full_path}')
# 输出报告
print(f'共扫描到 {len(report)} 个目标文件,{len(errors)} 个错误')
for item in report:
print(f"{item['path']} | {item['size_kb']}KB | {item['modified']}")
return report
if __name__ == '__main__':
scan_with_report('/data/project')
输出示例:
共扫描到 15 个目标文件,1 个错误
/data/project/logs/error.log | 2048.5KB | 2025-03-15 14:22:33
/data/project/readme.txt | 12.3KB | 2025-03-10 09:11:04
...
权限不足: /data/project/private/secret.log
性能优化:处理百万级文件目录
当目录包含数十万文件时,直接遍历会非常慢,以下是业界验证过的优化方案:
使用scandir()代替listdir()
Python 3.5+ 的os.scandir()返回迭代器,避免一次性加载所有文件名。
def fast_scan(root_dir):
result = []
with os.scandir(root_dir) as entries:
for entry in entries:
if entry.is_file():
result.append(entry.path)
elif entry.is_dir():
result.extend(fast_scan(entry.path))
return result
并发扫描(multiprocessing)
对大型挂载点(如NAS),可用进程池并行扫描子目录。
from multiprocessing import Pool
import os
def scan_subdir(dir_path):
files = []
for root, dirs, files_list in os.walk(dir_path):
for f in files_list:
files.append(os.path.join(root, f))
return files
def parallel_scan(root_dir, num_workers=4):
subdirs = [os.path.join(root_dir, d) for d in os.listdir(root_dir)
if os.path.isdir(os.path.join(root_dir, d))]
with Pool(num_workers) as pool:
results = pool.map(scan_subdir, subdirs)
# 合并结果
return [item for sublist in results for item in sublist]
注意:I/O密集型任务可能收益有限,建议先用timeit测试。
跳过无意义的目录
在os.walk()中修改dirs列表:
for root, dirs, files in os.walk('/data'):
# 跳过.git、node_modules等
dirs[:] = [d for d in dirs if d not in ('__pycache__', '.git', 'node_modules')]
常见问题与问答汇总
Q1:os.walk()和pathlib.rglob()哪个更快?
A:通常os.walk()稍快(底层C实现),但差距不大,如果不需要复杂的路径操作,os.walk()更高效,而pathlib在代码可读性和跨平台路径处理上更优。
Q2:如何跳过权限不足的目录?
A:在os.walk()中,使用try-except捕获PermissionError:
for root, dirs, files in os.walk(root_dir):
try:
# 处理文件
pass
except PermissionError:
print(f'跳过无权限目录: {root}')
del dirs[:] # 跳过该目录及其子目录
Q3:如何只扫描当前层(不递归)?
A:使用os.listdir()或Path.iterdir():
# pathlib版本
files = [p for p in Path('/data').iterdir() if p.is_file()]
Q4:扫描结果如何排序?
A:在获取文件列表后,用sorted()按需排序:
files = sorted(files, key=lambda x: os.path.getmtime(x), reverse=True) # 按修改时间降序
Q5:如何处理超长路径(Windows限制260字符)?
A:在Windows上,路径前加(需用原始字符串),Python的pathlib在3.6+会自动处理,但需确保文件系统支持,建议使用pathlib并开启长路径支持(Windows 10 1607+)。
总结与最佳实践
-
选择原则:
- 简单扫描:
os.walk()或pathlib.rglob() - 需要过滤/统计:
pathlib.rglob('*.py') - 高性能要求:
os.scandir()+ 并发
- 简单扫描:
-
必须处理异常:
- 权限错误(PermissionError)
- 文件被删除(FileNotFoundError)
- 符号链接死循环(
os.walk()默认忽略)
-
代码规范:
- 用
os.path.join或Path拼接路径,避免硬编码分隔符 - 遍历过程中不修改正在遍历的目录结构(会导致不可预测行为)
- 对生产环境,建议使用
logging记录错误而非print
- 用
-
资源清理:
- 使用
with语句打开资源(如os.scandir) - 大文件列表考虑写入文件而非全部保存在内存
- 使用
最后:如果您的需求是定期扫描文件变化,建议升级为watchdog库(基于事件监听),而非轮询遍历。
补充:以上代码已通过Python 3.8-3.12测试,如果您需要扫描网络共享目录(如SMB/NFS),建议先用os.path.ismount()检测挂载点,并考虑网络延迟的影响。