Python:从Office/Excel文档中访问嵌入式OLE而不使用剪贴板
我想使用Python从Office/Excel文档中添加和提取文件。到目前为止,添加东西很容易,但对于提取,我还没有找到一个干净的解决方案 为了弄清楚我得到了什么和没有得到什么,我编写了下面的小示例test.py,并进一步解释 test.pyPython:从Office/Excel文档中访问嵌入式OLE而不使用剪贴板,python,excel,com,ms-office,ole,Python,Excel,Com,Ms Office,Ole,我想使用Python从Office/Excel文档中添加和提取文件。到目前为止,添加东西很容易,但对于提取,我还没有找到一个干净的解决方案 为了弄清楚我得到了什么和没有得到什么,我编写了下面的小示例test.py,并进一步解释 test.py import win32com.client as win32 import os from tkinter import messagebox import win32clipboard # (0) Setup dir_path = os.path.
import win32com.client as win32
import os
from tkinter import messagebox
import win32clipboard
# (0) Setup
dir_path = os.path.dirname(os.path.realpath(__file__))
print(dir_path)
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(dir_path + "\\" + "test_excel.xlsx")
ws = wb.Worksheets.Item(1)
objs = ws.OLEObjects()
# (1) Embed file
f = dir_path + "\\" + "test_txt.txt"
name = "test_txt_ole.txt"
objs.Add( Filename=f, IconLabel=name )
# (2) Access embedded file
obj = objs.Item(1) # Get single OLE from OLE list
obj.Copy()
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData(0xC004) # Binary access
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
messagebox.showinfo(title="test_txt_ole.txt", message=str(data))
# (3) Press don't save here to keep
# wb.Close() # Will close excel document and leave excel opened.
excel.Application.Quit() # Will close excel with all opened documents
对于准备(步骤0),它打开一个给定的excel文档,其中包含一个以前使用excel中的“新建文档”按钮创建的工作表
在步骤(1)中,它使用API将给定的文本文件嵌入excel文档。之前使用文本编辑器创建了内容为“TEST123”的文本文件
然后在步骤(2)中,它尝试使用剪贴板从嵌入式OLE读回内容,并打开一个消息框,其中显示剪贴板中OLE的内容
最后(3)程序关闭打开的文档。要保持设置不变,请按此处的“否”
此解决方案的最大缺点是使用剪贴板,它会粉碎剪贴板中的任何用户内容,这在生产环境中是不好的样式。此外,它对剪贴板使用未记录的选项
更好的解决方案是将OLE或OLE嵌入文件安全地保存到python数据容器或我选择的文件中。在我的示例中,我使用了一个TXT文件来轻松识别文件数据。最后,我将使用ZIP作为一体式解决方案,但对于base64数据,TXT文件解决方案就足够了
0xC004=49156的来源:
这个VBA示例看起来很有趣,但我对VBA一无所知:考虑使用Windows临时目录,该目录将在嵌入工作簿时临时存储OLE对象的文件源。此解决方案中使用的不是剪贴板,而是物理文件 使用这种方法,您需要检索当前用户名并遍历临时目录的所有文件:C:\Documents and Settings\{username}\Local Settings\temp(适用于Windows Vista/7/8/10的标准Excel转储文件夹)。此外,还使用了在中使用
的类似条件的名称搜索,该搜索将原始文件的基本名称包含为多个版本,并带有数字后缀(1)、(2)、(3),。。。可能存在,具体取决于脚本运行的次数。甚至可以在这里尝试正则表达式搜索
最后,下面的例程使用try…except…Finally
块来干净地存在Excel对象,而不考虑错误,但将输出任何异常消息。请注意,这只是一个使用文本文件的Windows解决方案
import win32com.client as win32
import os, shutil
from tkinter import messagebox
# (0) Setup
dir_path = cd = os.path.dirname(os.path.abspath(__file__))
print(dir_path)
try:
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(os.path.join(dir_path, "test_excel.xlsx"))
ws = wb.Worksheets(1)
objs = ws.OLEObjects()
# (1) Embed file
f = os.path.join(dir_path, "test_txt.txt")
name = "test_txt_ole.txt"
objs.Add(Filename=f, IconLabel=name).Name = 'Test'
# (2) Open file from temporary folder
ole = ws.OLEObjects(1)
ole.Activate()
# (3) Grab the recent like-named file
user = os.environ.get('USERNAME')
outfile = os.path.join(dir_path, "test_txt_out.txt")
tempfolder = r"C:\Documents and Settings\{}\Local Settings\Temp".format(user)
for subdir, dirs, files in os.walk(tempfolder):
for file in sorted(files, reverse=True):
if 'test_txt' in file:
tempfile = os.path.join(tempfolder, file)
break
shutil.copyfile(tempfile, outfile)
# (4) Read text content
with open(outfile, 'r') as f:
content = f.readlines()
# (5) Output message with content
messagebox.showinfo(title="test_txt_ole.txt", message="".join(content))
except Exception as e:
print(e)
finally:
wb.Close(True) # CLOSES AND SAVES WORKBOOK
excel.Quit # QUITS EXCEL APP
# RELEASES COM RESOURCES
ws = None; wb = None; objs = None; ole = None; excel = None
Tkinter消息框
好吧,我觉得冻糕的解决方案有点老土(不好的意思),因为
- 它假定Excel将嵌入保存为临时文件
- 它假定此临时文件的路径始终是用户的默认临时路径
- 它假定您有权限在那里打开文件
- 它假设您使用命名约定来标识您的对象(例如,“test_txt”总是在名称中找到,您不能
插入对象“帐户\数据”)
- 它假定此约定不受操作系统的干扰(例如,它不会将其更改为“~test_tx(1)”以保存字符)
长度)
- 它假定计算机上的所有其他程序都知道并接受此约定(其他程序不会使用包含“test_txt”的名称)
所以,我写了一个替代方案。其实质是:
解压.xlsx文件(或基于XML的新版本中的任何其他Office文件)
格式(不受密码保护)设置为临时路径
遍历“/xxx/embeddings”(“xxx”)中的所有.bin文件=
“xl”或“word”或“ppt”),并创建包含.bin的字典
文件的临时路径作为键和从中返回的字典
第3步,作为值
从.bin文件中根据
详细记录)Ole Packager格式,并按以下方式返回信息:
一本字典。(将原始二进制数据检索为“内容”,而不仅仅是
来自.txt,但不包括任何文件类型,例如.png)
我仍在学习Python,所以这并不完美(没有错误检查,没有性能优化),但您可以从中获得想法。我用几个例子测试了它。
这是我的密码:
import tempfile
import os
import shutil
import zipfile
import glob
import pythoncom
import win32com.storagecon
def read_zipped_xml_bin_embeddings( path_zipped_xml ):
temp_dir = tempfile.mkdtemp()
zip_file = zipfile.ZipFile( path_zipped_xml )
zip_file.extractall( temp_dir )
zip_file.close()
subdir = {
'.xlsx': 'xl',
'.xlsm': 'xl',
'.xltx': 'xl',
'.xltm': 'xl',
'.docx': 'word',
'.dotx': 'word',
'.docm': 'word',
'.dotm': 'word',
'.pptx': 'ppt',
'.pptm': 'ppt',
'.potx': 'ppt',
'.potm': 'ppt',
}[ os.path.splitext( path_zipped_xml )[ 1 ] ]
embeddings_dir = temp_dir + '\\' + subdir + '\\embeddings\\*.bin'
result = {}
for bin_file in list( glob.glob( embeddings_dir ) ):
result[ bin_file ] = bin_embedding_to_dictionary( bin_file )
shutil.rmtree( temp_dir )
return result
def bin_embedding_to_dictionary( bin_file ):
storage = pythoncom.StgOpenStorage( bin_file, None, win32com.storagecon.STGM_READ | win32com.storagecon.STGM_SHARE_EXCLUSIVE )
for stastg in storage.EnumElements():
if stastg[ 0 ] == '\1Ole10Native':
stream = storage.OpenStream( stastg[ 0 ], None, win32com.storagecon.STGM_READ | win32com.storagecon.STGM_SHARE_EXCLUSIVE )
result = {}
result[ 'original_filename' ] = '' # original filename in ANSI starts at byte 7 and is null terminated
stream.Seek( 6, 0 )
while True:
ch = stream.Read( 1 )
if ch == '\0':
break
result[ 'original_filename' ] += ch
result[ 'original_filepath' ] = '' # original filepath in ANSI is next and is null terminated
while True:
ch = stream.Read( 1 )
if ch == '\0':
break
result[ 'original_filepath' ] += ch
stream.Seek( 4, 1 ) # next 4 bytes is unused
temporary_filepath_size = 0 # size of the temporary file path in ANSI in little endian
temporary_filepath_size |= ord( stream.Read( 1 ) ) << 0
temporary_filepath_size |= ord( stream.Read( 1 ) ) << 8
temporary_filepath_size |= ord( stream.Read( 1 ) ) << 16
temporary_filepath_size |= ord( stream.Read( 1 ) ) << 24
result[ 'temporary_filepath' ] = stream.Read( temporary_filepath_size ) # temporary file path in ANSI
result[ 'size' ] = 0 # size of the contents in little endian
result[ 'size' ] |= ord( stream.Read( 1 ) ) << 0
result[ 'size' ] |= ord( stream.Read( 1 ) ) << 8
result[ 'size' ] |= ord( stream.Read( 1 ) ) << 16
result[ 'size' ] |= ord( stream.Read( 1 ) ) << 24
result[ 'contents' ] = stream.Read( result[ 'size' ] ) # contents
return result
我构建了一个python模块来完成这项工作,请在这里查看。该模块也可以在任何操作系统上运行
安装库之后,请使用以下代码段
代码:
我最近试图回答一个类似的问题:我能从excel文件中提取嵌入式word文档并将其保存到磁盘吗
调整此页面上的答案(并利用excel文件是压缩的文件集合(主要是XML文件)的知识),这可以很容易地执行:
创建临时文件
将excel文件的所有内容提取到临时文件夹中
查找所有嵌入的文件
将嵌入的文件移动到您选择的永久文件夹中
以下是执行上述操作的代码段:
import zipfile
import tempfile
import os
import glob
import shutil
import sys
def extract_embedded_files(file_path,
save_path,
sub_dir='xl'):
"""
Extracts embedded files from Excel documents, it takes advantage of
excel being a zipped collection of files. It creates a temporary folder,
extracts all the contents of the excel folder there and then moves the
embedded files to the requested save_path.
Parameters:
----------
file_path : str,
The path to the excel file to extract embedded files from.
save_path : str,
Path to save the extracted files to.
sub_dir : str,
one of 'xl' (for excel), 'word' , or 'ppt'.
"""
# make a temporary directory
temp_dir = tempfile.mkdtemp()
# extract contents excel file to temporary dir
zip_file = zipfile.ZipFile(file_path)
zip_file.extractall(temp_dir)
zip_file.close()
# find all embedded files and copy to save_path
embeddings_dir = f'{temp_dir}/{sub_dir}/embeddings/'
embedded_files = list(glob.glob(embeddings_dir+'*'))
for file in embedded_files:
shutil.copy(file, save_path)
因此,我发现这个问题令人困惑。看起来你想从excel文件中提取一些东西,但我发现这些东西有点模糊。我不熟悉python,但事实上,你已经做了很多“VBA”<代码>工作簿。打开
,工作表。项
等是一种VBA命令(技术上它们是IDispatch调用)。为什么不直接从python中调用VBA示例中的oEmbFile.Object.SaveAs fName
code@Simon:Pyhton-like VBA使用COM接口。我可以使用下面的代码行查看对象API,但是没有SaveAs,只有Activate和Copy之类的东西。messagebox.showinfo(title=“packBootstrap”,message=“Item obj:\n”+str(type(obj))+str(dir(dir(obj)))messagebox.showinfo(title=“packBootstrap”,message=“oleobj:\n”+str(type(obj.\u oleobj))+str dir(dir(obj.\u oleobj))
from AttachmentsExtractor import extractor
abs_path_to_file='Please provide absolute path here '
path_to_destination_directory = 'Please provide path of the directory where the extracted attachments should be stored'
extractor.extract(abs_path_to_file,path_to_destination_directory) # returns true if one or more attachments are found else returns false.
import zipfile
import tempfile
import os
import glob
import shutil
import sys
def extract_embedded_files(file_path,
save_path,
sub_dir='xl'):
"""
Extracts embedded files from Excel documents, it takes advantage of
excel being a zipped collection of files. It creates a temporary folder,
extracts all the contents of the excel folder there and then moves the
embedded files to the requested save_path.
Parameters:
----------
file_path : str,
The path to the excel file to extract embedded files from.
save_path : str,
Path to save the extracted files to.
sub_dir : str,
one of 'xl' (for excel), 'word' , or 'ppt'.
"""
# make a temporary directory
temp_dir = tempfile.mkdtemp()
# extract contents excel file to temporary dir
zip_file = zipfile.ZipFile(file_path)
zip_file.extractall(temp_dir)
zip_file.close()
# find all embedded files and copy to save_path
embeddings_dir = f'{temp_dir}/{sub_dir}/embeddings/'
embedded_files = list(glob.glob(embeddings_dir+'*'))
for file in embedded_files:
shutil.copy(file, save_path)