【问题标题】:Call exiftool from a python script?从 python 脚本调用 exiftool?
【发布时间】:2012-04-09 14:58:05
【问题描述】:

我希望使用 exiftool 从我的照片和视频中扫描 EXIF 标签。它是一个 perl 可执行文件。对此进行推断的最佳方法是什么?是否有任何 Python 库可以做到这一点?还是我应该直接调用可执行文件并解析输出? (后者似乎很脏。)谢谢。

我问的原因是因为我目前使用的是 pyexiv2,它不支持视频。 Perl 的 exiftool 对图像和视频的支持非常广泛,我想使用它。

【问题讨论】:

标签: python exiftool


【解决方案1】:

为避免为每个图像启动一个新进程,您应该使用-stay_open 标志启动exiftool。然后,您可以通过标准输入向进程发送命令,并在标准输出上读取输出。 ExifTool 支持 JSON 输出,这可能是读取元数据的最佳选择。

这是一个简单的类,它启动一个exiftool 进程并具有一个execute() 方法来向该进程发送命令。我还加入了get_metadata() 以读取 JSON 格式的元数据:

import subprocess
import os
import json

class ExifTool(object):

    sentinel = "{ready}\n"

    def __init__(self, executable="/usr/bin/exiftool"):
        self.executable = executable

    def __enter__(self):
        self.process = subprocess.Popen(
            [self.executable, "-stay_open", "True",  "-@", "-"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        return self

    def  __exit__(self, exc_type, exc_value, traceback):
        self.process.stdin.write("-stay_open\nFalse\n")
        self.process.stdin.flush()

    def execute(self, *args):
        args = args + ("-execute\n",)
        self.process.stdin.write(str.join("\n", args))
        self.process.stdin.flush()
        output = ""
        fd = self.process.stdout.fileno()
        while not output.endswith(self.sentinel):
            output += os.read(fd, 4096)
        return output[:-len(self.sentinel)]

    def get_metadata(self, *filenames):
        return json.loads(self.execute("-G", "-j", "-n", *filenames))

此类被编写为上下文管理器,以确保在完成后退出进程。你可以把它当作

with ExifTool() as e:
    metadata = e.get_metadata(*filenames)

为 python 3 编辑: 为了让它在 python 3 中工作,需要做两个小改动。第一个是subprocess.Popen 的附加参数:

self.process = subprocess.Popen(
         [self.executable, "-stay_open", "True",  "-@", "-"],
         universal_newlines=True,
         stdin=subprocess.PIPE, stdout=subprocess.PIPE)

第二个是你要解码os.read()返回的字节序列:

output += os.read(fd, 4096).decode('utf-8')

为 Windows 编辑:要使其在 Windows 上运行,sentinel 需要更改为 "{ready}\r\n",即

sentinel = "{ready}\r\n"

否则程序将挂起,因为 execute() 中的 while 循环不会停止

【讨论】:

  • 谢谢,这很有帮助。我如何将命令传递给 exiftool 以使可执行文件“保持打开状态”?如果我将可执行文件放在一个名为 parseImage() 的函数中,它不会在每次调用该函数时重新打开吗?
  • @ensnare:我包含了一些代码。我会在几分钟内改进代码。
  • 是的,这很棒。非常感谢。
  • 为什么低级的os调用?
  • @Taymon:这是我在不停止第一个 EOF 的情况下完成这项工作的唯一方法。你有更好的建议吗?
【解决方案2】:

以此为参考...

import subprocess
import os
import json

class ExifTool(object):

    sentinel = "{ready}\n"

    def __init__(self, executable="/usr/bin/exiftool"):
        self.executable = executable

    def __enter__(self):
        self.process = subprocess.Popen(
            [self.executable, "-stay_open", "True",  "-@", "-"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        return self

    def  __exit__(self, exc_type, exc_value, traceback):
        self.process.stdin.write("-stay_open\nFalse\n")
        self.process.stdin.flush()

    def execute(self, *args):
        args = args + ("-execute\n",)
        self.process.stdin.write(str.join("\n", args))
        self.process.stdin.flush()
        output = ""
        fd = self.process.stdout.fileno()
        while not output.endswith(self.sentinel):
            output += os.read(fd, 4096)
        return output[:-len(self.sentinel)]

    def get_metadata(self, *filenames):
        return json.loads(self.execute("-G", "-j", "-n", *filenames))

...使用 Python 3.8.10 和 IPYTHON 返回跟随错误;

" AttributeError Traceback(最近 最后调用) 56 57 e = ExifTool() ---> 58 e.load_metadata_lookup('/u02/RECOVERY/')

在 load_metadata_lookup(self, locDir) 51 '\n 文件位置 > ', 文件位置, '\n') 52 ---> 53 self.get_metadata(FileLoc) 54 55

在 get_metadata(self, FileLoc) 38 39 def get_metadata(self, FileLoc): ---> 40 返回 json.loads(self.execute("-G", "-j", "-n", FileLoc)) 41 42

在执行(self, *args) 28 def执行(自我,*args): 29 args = args + ("-执行\n",) ---> 30 self.process.stdin.write(str.join("\n", args)) 31 self.process.stdin.flush() 32 输出 = ""

AttributeError: 'ExifTool' 对象没有属性 'process'

...

然后进行一些修改......成功!!! ... 修改和改编使用 [https://stackoverflow.com/users/279627/sven-marnach]

#!/usr/local/bin/python3
#! -*- coding: utf-8-mb4 -*-
from __future__ import absolute_import

import sys
import os
import subprocess
import json

headers_infos = """
.:.
.:. box33 | systems | platform |
.:. [   Renan Moura     ]
.:. [   ver.: 9.1.2-b   ]
.:.
"""

class ExifTool(object):
    sentinel = "{ready}\n"
    def __init__(self):
        self.executable         = "/usr/bin/exiftool"
        self.metadata_lookup    = {}

    def  __exit__(self, exc_type, exc_value, traceback):
        self.process.stdin.write("-stay_open\nFalse\n")
        self.process.stdin.flush()

    def execute(self, *args):
        self.process = subprocess.Popen([self.executable, "-stay_open", "True",  "-@", "-"],
            universal_newlines  = True                          ,
            stdin               = subprocess.PIPE               ,
            stdout              = subprocess.PIPE               ,
            stderr              = subprocess.STDOUT
        )

        args = (args + ("-execute\n",))

        self.process.stdin.write(str.join("\n", args))
        self.process.stdin.flush()

        output  = ""
        fd      = self.process.stdout.fileno()

        while not output.endswith(self.sentinel):
            output += os.read(fd, 4096).decode('utf-8')

        return output[:-len(self.sentinel)]

    def get_metadata(self, *FileLoc):
        return json.loads(self.execute("-G", "-j", "-n", *FileLoc))

    def load_metadata_lookup(self, locDir):
        self.metadata_lookup = {}
        for dirname, dirnames, filenames in os.walk(locDir):
            for filename in filenames:
                FileLoc=(dirname + '/' + filename)
                print(  '\n FILENAME    > ', filename,
                        '\n DIRNAMES    > ', dirnames,
                        '\n DIRNAME     > ', dirname,
                        '\n FILELOC     > ', FileLoc, '\n')

                self.metadata_lookup = self.get_metadata(FileLoc)
                print(json.dumps(self.metadata_lookup, indent=3))

e = ExifTool()
e.load_metadata_lookup('/u02/RECOVERY/')

...注意 这段代码来自“/u02/RECOVERY/” ...目录查找并在找到的每个文档上执行... 希望这可以帮助你...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    • 2014-06-25
    • 2018-03-25
    • 2020-06-25
    • 2021-10-19
    • 2016-03-02
    相关资源
    最近更新 更多