Python отслеживание изменения файла

Отслеживаем изменения в doc и docx при помощи Python

Здравствуйте, уважаемые коллеги! Прошу не судить строго, так как это моя первая статья.

У нас появилась необходимость отслеживать изменения в doc и docx файлах с фиксацией имени пользователя, который внес эти изменения. Сами файлы находятся в расшаренной папке (да, да, общие папки это зло, но убедить у меня не получилось) и необходимо знать кто внес изменения. Подробнее под катом.

Сами изменения в файлах будем отслеживать при помощи watchdog (pip install watchdog).

Код для отслеживания всех изменений:

# Для отслеживания изменений import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler # Отслеживаем изменения class Watcher: def __init__(self, path): self.observer = Observer() self.path = path def run(self): event_handler = Handler() self.observer.schedule(event_handler, self.path, recursive=True) self.observer.start() try: while True: time.sleep(1) except: self.observer.stop() print("Error") self.observer.join() class Handler(FileSystemEventHandler): @staticmethod def on_any_event(event): if event.is_directory: return None print( "[<>] noticed: [<>] on: [<>] ".format( time.asctime(), event.event_type, event.src_path ) ) if __name__ == "__main__": w = Watcher('C:\\Users\\user\\Desktop\\') w.run()

Такой код позволяет отслеживать изменения во всех файлах (не показывая кто изменил) на рабочем столе пользователя. Естественно, папку «C:\Users\user\Desktop\» эту указал для примера.

[Mon Dec 5 14:32:37 2022] noticed: [modified] on: [C:\Users\user\Desktop\Документ Microsoft Word.docx]

Тут нет информации о том, кто изменил файл. Эту информацию можно получить из самого docx файла.

Нам нужно отслеживать изменения именно в doc и docx файлах, поэтому переделаем код:

# -*- coding: utf-8 -*- # Для атрибутов файла import docx # Для отслеживания изменений import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler # Отслеживаем изменения class Watcher: def __init__(self, path): self.observer = Observer() self.path = path def run(self): event_handler = Handler() self.observer.schedule(event_handler, self.path, recursive=True) self.observer.start() try: while True: time.sleep(1) except: self.observer.stop() print("Error") self.observer.join() class Handler(FileSystemEventHandler): @staticmethod def on_any_event(event): # if event.is_directory: # return None # print( # "[<>] noticed: [<>] on: [<>] ".format( # time.asctime(), event.event_type, event.src_path # ) # ) # Получаем атрибуты файла if '.tmp' in event.src_path: return None try: document = docx.Document(docx = event.src_path) core_properties = document.core_properties print(f' в модифицировал файл ') except: pass if __name__ == "__main__": w = Watcher('C:\\Users\\user\\Desktop\\') w.run()

В данном примере изменения просто печатаются в консоль при помощи строки:

Иванов Иван Иванович в 2022-12-05 07:35:00 модифицировал файл C:\Users\user\Desktop\Документ Microsoft Word.docx

Для журналирования можно использовать простейшую запись в файл:

f = open('change-doc.log','w') # открытие в режиме записи f.write(f' в модифицировал файл \n') # запись в файл f.close() # закрытие файла

Или можно отправлять изменения на почту (если это востребовано):

import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText fromaddr = "test@mail.ru" toaddr = "test2@mail.ru" mypass = "password" msg = MIMEMultipart() msg['From'] = fromaddr msg['To'] = toaddr msg['Subject'] = f'Модифицирован файл ' body = f' в модифицировал файл ' msg.attach(MIMEText(body, 'plain')) server = smtplib.SMTP_SSL('smtp.mail.ru', 465) server.login(fromaddr, mypass) text = msg.as_string() server.sendmail(fromaddr, toaddr, text) server.quit()

Также приведу полный список атрибутов docx файла, которые можно таким же образом получить:

file_name = 'C:\\Users\\user\\Desktop\\123.docx' document = docx.Document(docx = file_name) core_properties = document.core_properties print('author', core_properties.author) print('created', core_properties.created) print('last_modified_by', core_properties.last_modified_by) print('last_printed', core_properties.last_printed) print('modified', core_properties.modified) print('revision', core_properties.revision) print('title', core_properties.title) print('category', core_properties.category) print('comments', core_properties.comments) print('identifier', core_properties.identifier) print('keywords', core_properties.keywords) print('language', core_properties.language) print('subject', core_properties.subject) print('version', core_properties.version) print('keywords', core_properties.keywords) print('content_status', core_properties.content_status)

Источник

Мониторинг за изменениями файловой системы

В поисках готового велосипеда для решения задачи мониторинга за изменениями в ФС с поддержкой linux+freebsd наткнулся на приятную python либу watchdog (github, packages.python.org). Которая помимо интересных мне ОС поддерживает также MacOS (есть своя специфика) и Windows.
Тем, кому данный вопрос интересен и кого не отпугнет индийское происхождение автора, прошу .

Установка

Можно взять готовую версию из PIP:
$ pip install watchdog
Сам PIP ставится как пакет python-pip, порт devel/py-pip, etc.
Либо собрать из исходников через setup.py.

Достаточно подробно все расписано в оригинальном руководстве. Правда там описание версии 0.5.4, а сейчас актуальна 0.6.0. Однако, вся разница в правке копирайтов и замене отступа в 4 пробела на отступ в 2. «Google code style» 🙂

Вообще, там довольно много особенностей сборки по версиям самого python так и по целевой платформе. Они все описаны по ссылке выше, но если будет нужно, допишу в статью вкратце на русском.

Кроме того, собрать модуль можно на несовместимой ОС, но тогда в дело вступится fallback-реализация, делающая «слепки» структуры ФС с последующими сравнениями. Возможно, так кто-то и делал у себя при решении подобной задачи 🙂

Сам же я пробовал собрать под ubuntu 11.4 и freebsd-8.2 RELEASE, каких-либо проблем при сборке и работе не возникло.

Базовый пример

Предположим, что нас интересуют изменения по некоему пути /path/to/smth, связанные с созданием, удалением и переименованием файлов и директорий.

from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler 

Класс Observer выбирается в /observers/__init__.py исходя из возможностей вашей ОС, так что нет необходимости самостоятельно решать, что же выбрать.
Класс FileSystemEventHandler является базовым классом обработчика событий изменения. Он мало что умеет, но мы научим его потомка:

class Handler(FileSystemEventHandler): def on_created(self, event): print event def on_deleted(self, event): print event def on_moved(self, event): print event 

Полный список методов можно увидеть в самом FileSystemEventHandler.dispatch: on_modified, on_moved, on_created, on_deleted.

observer = Observer() observer.schedule(Handler(), path='/path/to/smth', recursive=True) observer.start() 

Observer является относительно далеким потомком threading.Thread, соотвественно после вызова start() мы получаем фоновый поток, следящий за изменениями. Так что если скрипт сразу завершится, то ничего толкового мы не получим. Реалиация ожидания зависит в первую очередь от применения модуля в реальном проекте, сейчас же можно просто сделать костыль:

try: while True: time.sleep(0.1) except KeyboardInterrupt: observer.stop() observer.join() 

Ждем событий изменений ФС до прихода Ctrl+C (SIGINT), после чего говорим нашему потоку завершиться и ждем, пока он это выполнит.

Запускаем скрипт, идем по нашему пути и:

# mkdir foo # touch bar # mv bar baz # cd foo/ # mkdir foz # mv ../baz ./quz # cp ./quz ../hw # cd .. # rm -r ./foo # rm -f ./* 

В методы нашего класса Handler в поле event приходят потомки FileSystemEvent, перечисленные в watchdog/events.py.
У всех есть свойства src_path, is_directory, event_type («created», «deleted», и т.п.). Для события moved добавляется свойство dest_path.

Ну если вы больше ничего не хотите… А разве ещё что-нибудь есть?

  • * любые символы
  • ? любой единичный символ
  • [seq] любой единичный символ из указанных
  • [!seq] любой единичный символ НЕ из указанных
class Handler(PatternMatchingEventHandler): pass event_handler = Handler( patterns = ['*.py*'], ignore_patterns = ['cache/*'], ignore_directories = True, case_sensitive = False ) observer = Observer() observer.schedule(event_handler, path='/home/LOGS/', recursive=True) 

RegexMatchingEventHandler делает тоже самое, но с явным указанием regexp-выражений в конструкторе:

class Handler(RegexMatchingEventHandler): pass event_handler = Handler( regexes = ['\.py.?'], ignore_regexes = ['cache/.*'], ignore_directories = True, case_sensitive = False ) 

PatternMatchingEventHandler внутри себя в итоге транслирует шаблоны в регулярки, так что должен работать медленнее из-за наличия такого оверхеда.

Наконец, LoggingEventHandler выводит все в лог через logging.info().

— Вот и все. Может кому пригодится.

P.S.
При слежении за директорией, в которой (и в ее дочерних) содержатся папки/файлы не с ascii именованием, возникнет исключение exceptions.UnicodeEncodeError в глубинах watchdog’а. В Linux (inotify) он возникает в watchdog.observers.inotify.Inotify._add_watch.
Причина — чтение содержимого в ascii кодировке.
Для исправления ситуации можно пропатчить метод:

from watchdog.observers.inotify import Inotify _save = Inotify._add_watch Inotify._add_watch = lambda self, path, mask: _save(self, path.encode('utf-8'), mask) 

Вот пример исходной строки, и ее repr() до и после обработки encode():

/home/atercattus/.wine/drive_c/users/Public/Рабочий стол u'/home/atercattus/.wine/drive_c/users/Public/\u0420\u0430\u0431\u043e\u0447\u0438\u0439 \u0441\u0442\u043e\u043b' '/home/atercattus/.wine/drive_c/users/Public/\xd0\xa0\xd0\xb0\xd0\xb1\xd0\xbe\xd1\x87\xd0\xb8\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd0\xbb' 

Источник

Читайте также:  Preg match php проверка только числа
Оцените статью