Статья Извлекаем метаданные из фото, аудио и видео файлов с помощью Python

Давайте представим, что к вам попал некий файл: изображение, видео, а может быть аудиофайл, и вам нужно узнать о данном файле чуть больше информации чем отображается в свойствах файла. Если она там есть. Ведь современные камеры, смартфоны и прочее фото и видео оборудование добавляет их в файл в автоматическом режиме. И если файл не обработан программами, которые могут их затереть, то из таких метаданных, иногда, можно получить много разнообразной информации. В этом вам могут помочь специализированные программы, а можно воспользоваться небольшим скриптом на Python и получить необходимую информацию. Давайте создадим скрипт, который будет получать метаданные из фото, видео и аудио файлов.

52bd08545c8e0d74b54dac88d9027bc7.jpg


Что понадобиться?

Для работы с изображениями установим библиотеку Pillow. Именно с ее помощью мы будем получать метаданные, если они, конечно же есть. А также установим библиотеку ffmpeg, которая работает с очень большим количеством форматов как аудио, так и видео. Причем, извлекает метаданные из фото (правда не все). Пишем в терминале команду:

pip install Pillow ffmpeg-python

После того, как все библиотеки будут установлены, импортируем нужные модули в скрипт.

Python:
import os.path
from pprint import pprint

import ffmpeg
from PIL import Image, ExifTags

Давайте приступим к созданию скрипта.


Извлекаем метаданные из файлов JPG

Создадим функцию для извлечения метаданных из изображения. Я назвал ее image_metadata(path_f). На входе она принимает только один параметр, а именно путь к файлу изображения. Затем с помощью модуля Image библиотеки Pillow откроем файл. Создадим небольшой словарь, в который добавим базовые данные о фото, такие как имя, разрешение, ширина, высота и прочие. Эти данные будут выводиться пользователю в том случае, если метеданных в фото не оказалось или они не были прочитаны.

Python:
    img = Image.open(path_f)
    info_dict = {
            "Имя файла": os.path.split(path_f)[1],
            "Разрешение изображения": img.size,
            "Высота изображения": img.height,
            "Ширина изображения": img.width,
            "Формат изображения": img.format,
            "Режим изображения": img.mode,
            "Анимированное изображение": getattr(img, "is_animated", False),
            "Кадров в изображении": getattr(img, "n_frames", 1)
        }

Теперь, если данные exif были прочитаны и не было вызвано исключение, формируем словарь из полученных значений, к которому добавим в качестве ключа название тега. Для этого будем использовать параметр ExifTags.TAGS. Если вы распечатаете данный параметр, на выходе получите словарь с тегами, которые могут содержаться в изображении.

После того, как словарь будет сформирован, нужно вывести полученные значения пользователю на экран. Пробегаемся в цикле по словарю exif. Здесь, если мы получаем значение тега «GPSInfo», печатаем данные немного по другому, так как за данным тегом есть еще словарь со значениями широты, долготы, направления и прочие параметры. Для наших целей достаточно будет получить значение широты и долготы. Поэтому указываем значения json, где они содержаться, для вывода на экран.

Также, некоторые значения в json представлены в байтах, а потому из надо декодировать. Но, если мы будем декодировать все значения, то на экран ничего не выведется. Поэтому делаем проверку, является ли данный параметр байтами. Если да, декодируем и выводим уже декодированное значение.

Python:
        exif = {ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS}

        print(f'\n[+] Метаданные фото: {os.path.split(path_f)[1]:27}\n')
        for info in exif:
            if info == 'GPSInfo':
                print(f'{info:27}: lat {exif[info][2]} {exif[info][1]} - long {exif[info][4]} {exif[info][3]}')
            else:
                if isinstance(exif[info], bytes):
                    info_d = exif[info].decode()
                    print(f'{info:25}: {info_d}')
                else:
                    print(f'{info:25}: {exif[info]}')

Как вы помните, в изображении тегов может и не содержаться. Поэтому будет вызвано исключение AttributeError. Но, в самом начале функции мы создавали словарь с базовыми параметрами изображения. Вот их и выведем в случае, когда сработает исключение и метаданных в изображении найдено не будет. Поэтому, оборачиваем код в try — except и когда сработает исключение пробегаемся в цикле по созданному ранее словарику из которого извлекаем значения и выводим на печать.

Python:
        print(f'\n[+] Информация о фото: {os.path.split(path_f)[1]:27}\n')
        for k, v in info_dict.items():
            print(f"{k:27}: {v}")
        exit(0)

Python:
def image_metadata(path_f):
    img = Image.open(path_f)
    info_dict = {
            "Имя файла": os.path.split(path_f)[1],
            "Разрешение изображения": img.size,
            "Высота изображения": img.height,
            "Ширина изображения": img.width,
            "Формат изображения": img.format,
            "Режим изображения": img.mode,
            "Анимированное изображение": getattr(img, "is_animated", False),
            "Кадров в изображении": getattr(img, "n_frames", 1)
        }
    try:
        exif = {ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS}

        print(f'\n[+] Метаданные фото: {os.path.split(path_f)[1]:27}\n')
        for info in exif:
            if info == 'GPSInfo':
                print(f'{info:27}: lat {exif[info][2]} {exif[info][1]} - long {exif[info][4]} {exif[info][3]}')
            else:
                if isinstance(exif[info], bytes):
                    info_d = exif[info].decode()
                    print(f'{info:25}: {info_d}')
                else:
                    print(f'{info:25}: {exif[info]}')
    except AttributeError:
        print(f'\n[+] Информация о фото: {os.path.split(path_f)[1]:27}\n')
        for k, v in info_dict.items():
            print(f"{k:27}: {v}")
        exit(0)


Извлекаем метаданные из видео и аудио

Для извлечения метаданных из аудио и видео воспользуемся библиотекой для работы с медиафайлами ffmpeg. Конечно же, функционал данной библиотеки гораздо больше, чем просто извлечение метаданных. Но, нам требуется далеко не все, а потому, если вы желаете чуть более подробно познакомиться с данной библиотекой, можете начать вот с этой статьи.

Двигаемся дальше. Для того, чтобы работать с данной библиотекой в скрипте, нужно ее для начала установить. Если вы используете Linux, просто наберите команду:

sudo apt-get install ffmpeg

Если же вам нужно установить данную библиотеку на Windows, перейдите на эту , скачайте и установите файл.

После того, как данная библиотека будет установлена в вашу операционную систему, нужно установить модуль обертку, с помощью которого мы и будем работать с ffmpeg. На самом деле, модулей для работы с ffmpeg очень много. Но мы воспользуемся ffmpeg-python. Если вы читали статью с самого начала, то он у вас уже будет установлен. Если же нет, то наберите в терминале:

pip install ffmpeg-python

Теперь создадим функцию для работы с файлами. Я назвал ее vid_aud_matadata(patn_f). На вход она принимает путь к файлу. После пытается считать метаданные. И если это удается, с помощью pprint мы выводим данные на печать в необработанном виде. А именно, в формате json. При желании вы можете написать функцию, которая будет сохранять данные значения в файл json.

Python:
        print(f'\n[+] Метаданные файла: {os.path.split(patn_f)[-1]}\n')
        pprint(ffmpeg.probe(patn_f)["streams"])

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

Python:
def vid_aud_matadata(patn_f):
    try:
        print(f'\n[+] Метаданные файла: {os.path.split(patn_f)[-1]}\n')
        pprint(ffmpeg.probe(patn_f)["streams"])
    except ffmpeg._run.Error:
        print('[-] Неподдерживаемый формат')

Как видите, извлечение метаданных из изображений, аудио или видео, не такое уж сложное занятие. С помощью данного скрипта можно извлечь метаданные из изображения, если они в нем содержаться, а также метаданные из видео и аудио файлов.


Функция обработки пользовательского ввода

Ну и теперь, когда функции для извлечения метаданных у нас уже созданы, напишем функцию для обработки пользовательского ввода. Для начала будем запрашивать путь к медиафайлу. Проверяем, существует ли данный файл. Если файл не существует, сообщаем об этом пользователю. Если же он существует, проверяем его расширение. И если расширение равно «.jpg» или «.jpeg» вызываем функцию извлечения метаданных из изображения, в которую передаем путь к файлу. Если же расширение не является изображением нужного нам формата, вызываем функцию обработки аудио и видео файлов, куда также передаем путь к файлу.

Python:
if __name__ == "__main__":
    path_file = input('[~] Введите путь к файлу: ')
    if not os.path.exists(path_file):
        print('[-] Файла не существует')
    else:
        if path_file.endswith(".jpg"):
            image_metadata(path_file)
        elif path_file.endswith(".jpeg"):
            image_metadata(path_file)
        else:
            vid_aud_matadata(path_file)

Как вы помните, ffmpeg может извлечь метаданные из файлов множества медиаформатов. И если к нему попадет картинка в формате «png», то метаданные из нее будут извлечены и выведены на экран. Тут смысл в том, что в основном, метаданные, такие как координаты, сведения о камере и прочее содержаться в основном в формате «jpg». Хотя, исключать их наличие в других форматах не совсем правильно.

Вот, для примера, метаданные, которые получены из фото, которое я сделал на телефон, предварительно включив перед этим геоданные.

img_01.png

А это небольшая часть метаданных из видео, которое скачано из YouTube.

img_02.png

Python:
# pip install Pillow ffmpeg-python

import os.path
from pprint import pprint

import ffmpeg
from PIL import Image, ExifTags


def image_metadata(path_f):
    img = Image.open(path_f)
    info_dict = {
            "Имя файла": os.path.split(path_f)[1],
            "Разрешение изображения": img.size,
            "Высота изображения": img.height,
            "Ширина изображения": img.width,
            "Формат изображения": img.format,
            "Режим изображения": img.mode,
            "Анимированное изображение": getattr(img, "is_animated", False),
            "Кадров в изображении": getattr(img, "n_frames", 1)
        }
    try:
        exif = {ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS}

        print(f'\n[+] Метаданные фото: {os.path.split(path_f)[1]:27}\n')
        for info in exif:
            if info == 'GPSInfo':
                print(f'{info:27}: lat {exif[info][2]} {exif[info][1]} - long {exif[info][4]} {exif[info][3]}')
            else:
                if isinstance(exif[info], bytes):
                    info_d = exif[info].decode()
                    print(f'{info:25}: {info_d}')
                else:
                    print(f'{info:25}: {exif[info]}')
    except AttributeError:
        print(f'\n[+] Информация о фото: {os.path.split(path_f)[1]:27}\n')
        for k, v in info_dict.items():
            print(f"{k:27}: {v}")
        exit(0)


def vid_aud_matadata(patn_f):
    try:
        print(f'\n[+] Метаданные файла: {os.path.split(patn_f)[-1]}\n')
        pprint(ffmpeg.probe(patn_f)["streams"])
    except ffmpeg._run.Error:
        print('[-] Неподдерживаемый формат')


if __name__ == "__main__":
    path_file = input('[~] Введите путь к файлу: ')
    if not os.path.exists(path_file):
        print('[-] Файла не существует')
    else:
        if path_file.endswith(".jpg"):
            image_metadata(path_file)
        elif path_file.endswith(".jpeg"):
            image_metadata(path_file)
        else:
            vid_aud_matadata(path_file)

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
 
Последнее редактирование:
  • Нравится
Реакции: Festeinfo
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!