Python: обход директорий и поиск нужных файлов
Поиск файлов в файловой системе - частая задача при работе с Python. В зависимости от сложности запроса (имя, расширение, размер, содержимое) и от структуры каталогов выбирается подходящий инструмент. В этой статье рассматриваются основные методы с примерами кода и разбором типичных ошибок.
Основные подходы к поиску файлов
Как эффективно найти файл по шаблону имени, включая все вложенные папки?
Наиболее современный и рекомендуемый способ - использование объекта Path из модуля pathlib и его метода rglob. Этот метод рекурсивно обходит все подкаталоги и возвращает пути, соответствующие шаблону.
from pathlib import Path
# Создаём объект Path для корневой папки
root = Path('.')
# Ищем все файлы с расширением .txt
for file_path in root.rglob('*.txt'):
print(file_path)
ввод программ на python (ввод данных в программе python)
Пример вывода (для папки с двумя файлами .txt):
./data/file1.txt ./docs/readme.txt
Python file io (ввод-вывод файлов в python)
Метод rglob принимает строку с шаблоном (используются правила glob: * - любая последовательность символов, ? - один символ, [abc] - один из символов). Для поиска по части имени можно использовать шаблон *запрос*.
Возможные проблемы:
- PermissionError - возникает при отсутствии прав на чтение каталога. Решение - обернуть вызов в try-except или предварительно проверять доступность через
os.access. - Символические ссылки -
rglobпо умолчанию не переходит по ссылкам (не зацикливается), но может выдавать их как файлы. Если нужно игнорировать ссылки, используйтеPath.is_symlink(). - Большие деревья - полный обход может быть медленным. Для ускорения используйте фильтрацию на лету или параллельную обработку (см. расширенные примеры).
Как найти файлы, используя старый модуль os и os.walk?
Классический метод - функция os.walk, которая рекурсивно обходит дерево каталогов. Применяется для гибкого контроля над процессом:
import os
for root_dir, dirs, files in os.walk('.'):
for file in files:
if file.endswith('.txt'):
print(os.path.join(root_dir, file))
Python temp files (временные файлы в python)
Здесь root_dir - текущая папка, dirs - список подкаталогов (можно модифицировать для исключения), files - имена файлов.
Особенности: os.walk возвращает имена без указания типа (файл/папка), требуется фильтрация вручную. Проблема с символическими ссылками: по умолчанию не переходит, но если ссылка ведёт на каталог, можно попасть в цикл, изменив список dirs. Также возможна ошибка доступа - каталог пропускается автоматически.
Как найти файлы с помощью модуля glob без рекурсии?
Модуль glob предоставляет функцию glob для поиска по шаблону в одной папке. Для рекурсии используйте ** и параметр recursive=True (Python 3.5+):
import glob
# Поиск в текущей папке
for name in glob.glob('*.txt'):
print(name)
# Рекурсивный поиск во всех подпапках
for name in glob.glob('**/*.txt', recursive=True):
print(name)
Python index files (индексация файлов в python)
Ошибки: recursive=True работает не во всех версиях Python (требуется 3.5+). В Windows символ ** может вызывать проблемы с длинными путями. Лучше использовать pathlib для кроссплатформенности.
Как отфильтровать файлы по нескольким критериям (имя, размер, дата)?
Комбинируйте os.walk (или pathlib) с проверками через os.stat или методы Path:
from pathlib import Path
import time
root = Path('.').resolve()
# Ищем файлы больше 1 МБ, изменённые за последние 7 дней
for p in root.rglob('*'):
if p.is_file() and p.stat().st_size > 1_000_000:
age = time.time() - p.stat().st_mtime
if age <= 7 * 24 * 3600:
print(p, p.stat().st_size)
File python class (класс для работы с файлами в python)
Проблема: при большом количестве файлов повторные вызовы stat замедляют поиск. Решение - кэшировать результаты в словаре или использовать os.scandir.
Как найти файл по точному имени, не просматривая все каталоги?
Если имя известно, можно использовать pathlib и rglob с полным именем или воспользоваться os.path.exists для прямого пути. Но для поиска в неизвестной папке - только обход:
from pathlib import Path
for p in Path('.').rglob('myfile.pdf'):
print(p)
break # нашли первое совпадение
Для ускорения можно завершить поиск после нахождения первого файла.
Расширенные примеры поиска файлов
Поиск файлов по размеру (больше/меньше/равно)
from pathlib import Path
root = Path('/home/user/projects')
size_limit = 100 * 1024 * 1024 # 100 MB
big_files = [f for f in root.rglob('*') if f.is_file() and f.stat().st_size > size_limit]
for f in big_files:
print(f, f.stat().st_size)
/home/user/projects/data/bigfile.bin 104857600 /home/user/projects/backup/archive.zip 125829120
Пояснение: используется метод stat().st_size для получения размера в байтах. Для производительности можно предварительно отфильтровать по расширению, если размер важен только для определённых типов.
Поиск файлов по дате последнего изменения (за последние N дней)
from pathlib import Path
import time
root = Path('/var/log')
now = time.time()
last_7_days = now - 7 * 24 * 3600
recent_files = [f for f in root.rglob('*')
if f.is_file() and f.stat().st_mtime > last_7_days]
for f in recent_files:
print(f, time.ctime(f.stat().st_mtime))
/var/log/syslog Thu Apr 10 08:12:34 2025 /var/log/auth.log Thu Apr 10 09:45:12 2025
Пояснение: st_mtime - время последнего изменения файла (timestamp). Сравнивается с текущим временем минус 7 дней.
Поиск файлов по содержимому (текстовый поиск)
from pathlib import Path
root = Path('/home/user/docs')
search_term = 'Python'
for file in root.rglob('*.txt'):
try:
content = file.read_text(encoding='utf-8')
if search_term in content:
print(f'Найдено в {file}')
except (UnicodeDecodeError, PermissionError):
pass
Найдено в /home/user/docs/notes.txt Найдено в /home/user/docs/tutorial.txt
Пояснение: для текстовых файлов используется read_text. Ошибки кодировки и доступа игнорируются. Для больших файлов лучше читать построчно или использовать mmap.
Рекурсивный поиск с фильтрацией по типу (папки/файлы)
from pathlib import Path
root = Path('.')
# Найти только папки (каталоги)
folders = [d for d in root.rglob('*') if d.is_dir()]
print('Папки:')
for d in folders[:5]:
print(d)
# Найти только файлы (исключая символические ссылки)
files = [f for f in root.rglob('*') if f.is_file() and not f.is_symlink()]
print('\nФайлы (первые 5):')
for f in files[:5]:
print(f)
Папки: . ./data ./src ./tests ./docs Файлы (первые 5): ./main.py ./config.json ./data/input.csv ./src/utils.py ./tests/test_main.py
Поиск файлов внутри ZIP-архива (без распаковки)
import zipfile
from pathlib import Path
archive_path = Path('archive.zip')
search_name = 'readme.txt'
with zipfile.ZipFile(archive_path, 'r') as zf:
for info in zf.infolist():
if search_name in info.filename:
print(f'Найден в архиве: {info.filename}, размер: {info.file_size}')
Найден в архиве: docs/readme.txt, размер: 1024
Пояснение: модуль zipfile позволяет сканировать содержимое архива без распаковки. Для поиска по содержимому файла внутри ZIP можно использовать zf.read(info.filename).
Параллельный поиск с использованием concurrent.futures (ускорение на многоядерных системах)
from pathlib import Path
from concurrent.futures import ProcessPoolExecutor, as_completed
root = Path('/home/user')
def search_in_directory(dir_path):
# Поиск всех .py файлов в одной директории
return [str(p) for p in dir_path.glob('*.py')]
subdirs = [d for d in root.iterdir() if d.is_dir()]
results = []
with ProcessPoolExecutor() as executor:
futures = {executor.submit(search_in_directory, d): d for d in subdirs}
for future in as_completed(futures):
results.extend(future.result())
print(f'Найдено {len(results)} .py файлов')
for r in results[:10]:
print(r)
Найдено 42 .py файлов /home/user/script1.py /home/user/main.py /home/user/module.py ...
Пояснение: разбиваем задачу по подкаталогам верхнего уровня и запускаем поиск параллельно. Эффективно при большом количестве папок. Для глубокой рекурсии можно использовать более сложное разбиение.