我已经找到了一些示例,可以处理目录中很少量的文件。我发现所有的方法都涉及打开一个存储容器对象。我是Python新手,不知道什么是存储容器对象。我只知道当我尝试使用它时,大多数文件会出错。
pythoncom.StgOpenStorage(<File Name>, None, flags)
只有少数能够正常工作,我可以获取主要的元数据标签,例如标题、主题、作者、创建时间等。
除了存储容器之外,还有其他方法可以获取元数据吗?如果有其他语言更容易实现这一点的方法,请务必提出建议。
谢谢
pythoncom.StgOpenStorage(<File Name>, None, flags)
只有少数能够正常工作,我可以获取主要的元数据标签,例如标题、主题、作者、创建时间等。
除了存储容器之外,还有其他方法可以获取元数据吗?如果有其他语言更容易实现这一点的方法,请务必提出建议。
谢谢
import win32com.client
sh=win32com.client.gencache.EnsureDispatch('Shell.Application',0)
ns = sh.NameSpace(r'm:\music\Aerosmith\Classics Live!')
colnum = 0
columns = []
while True:
colname=ns.GetDetailsOf(None, colnum)
if not colname:
break
columns.append(colname)
colnum += 1
for item in ns.Items():
print (item.Path)
for colnum in range(len(columns)):
colval=ns.GetDetailsOf(item, colnum)
if colval:
print('\t', columns[colnum], colval)
item = ns.ParseName('song.mp4')
而不是循环。实际上,如果您有from pathlib import Path
,则尝试file = Path(r'C:\path\to\file.ext')
并使用ns = sh.NameSpace(str(file.parent))
,item = ns.ParseName(str(file.name))
。 - Greedo为了试图将上面的答案结合并澄清,我决定写下自己的答案(这些答案在很大程度上帮助了我解决问题)。
我认为解决这个问题有两种方法。
情况1:你知道文件包含哪些元数据(你感兴趣的元数据)。
在这种情况下,假设您有一个字符串列表,其中包含您感兴趣的元数据。我在这里假设这些标签是正确的(即您对.txt文件的像素数不感兴趣)。
metadata = ['Name', 'Size', 'Item type', 'Date modified', 'Date created']
def get_file_metadata(path, filename, metadata):
# Path shouldn't end with backslash, i.e. "E:\Images\Paris"
# filename must include extension, i.e. "PID manual.pdf"
# Returns dictionary containing all file metadata.
sh = win32com.client.gencache.EnsureDispatch('Shell.Application', 0)
ns = sh.NameSpace(path)
# Enumeration is necessary because ns.GetDetailsOf only accepts an integer as 2nd argument
file_metadata = dict()
item = ns.ParseName(str(filename))
for ind, attribute in enumerate(metadata):
attr_value = ns.GetDetailsOf(item, ind)
if attr_value:
file_metadata[attribute] = attr_value
return file_metadata
# *Note: you must know the total path to the file.*
# Example usage:
if __name__ == '__main__':
folder = 'E:\Docs\BMW'
filename = 'BMW series 1 owners manual.pdf'
metadata = ['Name', 'Size', 'Item type', 'Date modified', 'Date created']
print(get_file_metadata(folder, filename, metadata))
使用以下内容:
{'Name': 'BMW series 1 owners manual.pdf', 'Size': '11.4 MB', 'Item type': 'Foxit Reader PDF Document', 'Date modified': '8/30/2020 11:10 PM', 'Date created': '8/30/2020 11:10 PM'}
这是正确的,因为我刚创建了该文件并使用Foxit PDF阅读器作为我的主要pdf阅读器。 因此,此函数返回一个字典,其中键是元数据标签,值是给定文件的那些标签的值。
情况2:您不知道文件包含哪些元数据
这是一个稍微困难一些的情况,特别是在优化方面。我分析了Roger Upole提出的代码,基本上他试图读取None
文件的元数据,结果得到了所有可能的元数据标签列表。所以我想可能更容易的办法是硬拷贝这个列表,然后尝试读取每个标签。这样,完成后,您将拥有一个包含文件实际拥有的所有标签的字典。
只需复制我认为是每个可能的元数据标签,并尝试从文件中获取所有标签。 基本上,只需复制这个声明Python列表的代码,并使用上面的代码(将metadata替换为这个新列表):
metadata = ['Name', 'Size', 'Item type', 'Date modified', 'Date created', 'Date accessed', 'Attributes', 'Offline status', 'Availability', 'Perceived type', 'Owner', 'Kind', 'Date taken', 'Contributing artists', 'Album', 'Year', 'Genre', 'Conductors', 'Tags', 'Rating', 'Authors', 'Title', 'Subject', 'Categories', 'Comments', 'Copyright', '#', 'Length', 'Bit rate', 'Protected', 'Camera model', 'Dimensions', 'Camera maker', 'Company', 'File description', 'Masters keywords', 'Masters keywords']
我不认为这是一个很好的解决方案,但另一方面,你可以把这个列表作为全局变量,并且无需在每次函数调用时传递它。为了完整起见,以下是使用这个新元数据列表的前一个函数的输出:
{'Name': 'BMW series 1 owners manual.pdf', 'Size': '11.4 MB', 'Item type': 'Foxit Reader PDF Document', 'Date modified': '8/30/2020 11:10 PM', 'Date created': '8/30/2020 11:10 PM', 'Date accessed': '8/30/2020 11:10 PM', 'Attributes': 'A', 'Perceived type': 'Unspecified', 'Owner': 'KEMALS-ASPIRE-E\\kemal', 'Kind': 'Document', 'Rating': 'Unrated'}
如你所见,现在返回的字典包含了文件中所有的元数据。 这个方法可行的原因是由于 if 语句:
if attribute_value:
这意味着每当一个属性等于None
时,它不会被添加到返回的字典中。
需要强调的是,如果要处理许多文件,在函数中传递列表而不是在每次调用函数时传递它将更加高效。
meta = open('myfile.ext:StreamName').read()
< p > < em > 更新: 好的,现在我看到这些都不相关,因为您需要的是< em>文档元数据而不是< em>文件元数据。问题清晰明了可以带来很大的差异:|
请尝试这个链接:如何在Python中检索办公文件的作者?
Windows API Code Pack可以与Python for .NET一起使用,用于读写文件元数据。
使用压缩工具(如7-Zip)提取.nupkg文件到脚本路径或系统路径变量中定义的某个位置。
使用pip install pythonnet
安装Python for .NET。
获取并设置MP4视频标题的示例代码:
import clr
clr.AddReference("Microsoft.WindowsAPICodePack")
clr.AddReference("Microsoft.WindowsAPICodePack.Shell")
from Microsoft.WindowsAPICodePack.Shell import ShellFile
# create shell file object
f = ShellFile.FromFilePath(r'movie..mp4')
# read video title
print(f.Properties.System.Title.Value)
# set video title
f.Properties.System.Title.Value = 'My video'
使用Hack来检查可用的属性:
dir(f.Properties.System)
我知道这是一个老问题,但我曾经遇到过同样的问题,并最终创建了一个软件包来解决我的问题:windows-metadata。
附带一提,Roger Upole的答案是一个很好的起点,但它并没有捕捉到文件可以拥有的所有属性(如果不是列名,那么中断会太早结束循环,因为Windows跳过了某些列号,出于某种原因。所以Roger的答案只给出了前30个左右的属性,实际上有近320个)。
现在,使用这个软件包来回答问题:
from windows_metadata import WindowsAttributes
attr = WindowsAttributes(<File Name>) # this will load all the filled attributes a file has
title = attr["Title"] # dict-like access
title = attr.title # attribute like access -> these two will return the same value
subject = attr.subject
author = attr.author
...
同样适用于文件具有的任何可用属性。
win32com
读取XLS文件属性。 Workbook
对象有一个BuiltinDocumentProperties
。
https://gist.github.com/justengel/87bac3355b1a925288c59500d2ce6ef5
import os
import win32com.client # Requires "pip install pywin32"
__all__ = ['get_xl_properties', 'get_file_details']
# https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.tools.excel.workbook.builtindocumentproperties?view=vsto-2017
BUILTIN_XLS_ATTRS = ['Title', 'Subject', 'Author', 'Keywords', 'Comments', 'Template', 'Last Author', 'Revision Number',
'Application Name', 'Last Print Date', 'Creation Date', 'Last Save Time', 'Total Editing Time',
'Number of Pages', 'Number of Words', 'Number of Characters', 'Security', 'Category', 'Format',
'Manager', 'Company', 'Number of Btyes', 'Number of Lines', 'Number of Paragraphs',
'Number of Slides', 'Number of Notes', 'Number of Hidden Slides', 'Number of Multimedia Clips',
'Hyperlink Base', 'Number of Characters (with spaces)']
def get_xl_properties(filename, xl=None):
"""Return the known XLS file attributes for the given .xls filename."""
quit = False
if xl is None:
xl = win32com.client.DispatchEx('Excel.Application')
quit = True
# Open the workbook
wb = xl.Workbooks.Open(filename)
# Save the attributes in a dictionary
attrs = {}
for attrname in BUILTIN_XLS_ATTRS:
try:
val = wb.BuiltinDocumentProperties(attrname).Value
if val:
attrs[attrname] = val
except:
pass
# Quit the excel application
if quit:
try:
xl.Quit()
del xl
except:
pass
return attrs
def get_file_details(directory, filenames=None):
"""Collect the a file or list of files attributes.
Args:
directory (str): Directory or filename to get attributes for
filenames (str/list/tuple): If the given directory is a directory then a filename or list of files must be given
Returns:
file_attrs (dict): Dictionary of {filename: {attribute_name: value}} or dictionary of {attribute_name: value}
if a single file is given.
"""
if os.path.isfile(directory):
directory, filenames = os.path.dirname(directory), [os.path.basename(directory)]
elif filenames is None:
filenames = os.listdir(directory)
elif not isinstance(filenames, (list, tuple)):
filenames = [filenames]
if not os.path.exists(directory):
raise ValueError('The given directory does not exist!')
# Open the com object
sh = win32com.client.gencache.EnsureDispatch('Shell.Application', 0) # Generates local compiled with make.py
ns = sh.NameSpace(os.path.abspath(directory))
# Get the directory file attribute column names
cols = {}
for i in range(512): # 308 seemed to be max for excel file
attrname = ns.GetDetailsOf(None, i)
if attrname:
cols[i] = attrname
# Get the information for the files.
files = {}
for file in filenames:
item = ns.ParseName(os.path.basename(file))
files[os.path.abspath(item.Path)] = attrs = {} # Store attributes in dictionary
# Save attributes
for i, attrname in cols.items():
attrs[attrname] = ns.GetDetailsOf(item, i)
# For xls file save special properties
if os.path.splitext(file)[-1] == '.xls':
xls_attrs = get_xl_properties(item.Path)
attrs.update(xls_attrs)
# Clean up the com object
try:
del sh
except:
pass
if len(files) == 1:
return files[list(files.keys())[0]]
return files
if __name__ == '__main__':
import argparse
P = argparse.ArgumentParser(description="Read and print file details.")
P.add_argument('filename', type=str, help='Filename to read and print the details for.')
P.add_argument('-v', '--show-empty', action='store_true', help='If given print keys with empty values.')
ARGS = P.parse_args()
# Argparse Variables
FILENAME = ARGS.filename
SHOW_EMPTY = ARGS.show_empty
DETAILS = get_file_details(FILENAME)
print(os.path.abspath(FILENAME))
for k, v in DETAILS.items():
if v or SHOW_EMPTY:
print('\t', k, '=', v)
StgOpenStorage
打开Windows结构化存储文件(以及旧式OLE复合文件),并且可以获取该格式定义的元数据。 - abarnert