Python:クリップボードなしでOffice / Excelドキュメントから埋め込み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.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で新規文書ボタンを使用することによって以前に作成された1つのワークシートを持つ特定のExcel文書を開きます。
ステップ(1)では、APIを使用して特定のテキストファイルをExcelドキュメントに埋め込みます。テキストファイルは、テキストエディタを使用してコンテンツ “TEST123″で以前に作成されました。その後、ステップ(2)でクリップボードを使用して埋め込みOLEからコンテンツを読み返そうとし、クリップボードのOLEからコンテンツを表示するメッセージボックスを開きます。最後に(3)プログラムは開かれた文書を閉じる。変更しない設定を維持するには、ここでnoを押します。

このソリューションの大きな欠点は、クリップボード内のユーザーコンテンツを破壊するクリップボードの使用です。これは、生産的な環境では不適切なスタイルです。さらにそれはクリップボードのために文書化されていないオプションを使用します。

よりよい解決策は、安全なOLEまたはOLE埋め込みファイルをpythonデータコンテナまたは私が選んだファイルにすることです。私の例では、ファイルデータを簡単に識別するためにTXTファイルを使用しました。最後に、オールインワンソリューションにはZIPを使用しますが、base64データにはTXTファイルソリューションで十分です。

0xC004のソース= 49156:https://danny.fyi/embedding-and-accessing-a-file-in-excel-with-vba-and-ole-objects-4d4e7863cfff

このVBAの例は面白そうですが、VBAについてはわかりません。Saving embedded OLE Object (Excel doc) to file in Excel 2010 vs 2013

ベストアンサー
さて、私はParfaitの解決策が少し悪い(悪い意味で)ハッキングだと思う。

> Excelは埋め込みを一時ファイルとして保存すると仮定します。
>この一時ファイルのパスは常にユーザーのデフォルトの一時パスであると仮定します。
>そこにファイルを開く特権があると仮定します。
>それはあなたがあなたのオブジェクトを識別するために命名規則を使うことを仮定しています(例えば ‘test_txt’は常に名前の中にあります、あなたはできません)
オブジェクト「account_data」を挿入します。
>それは、この規約がオペレーティングシステムによって妨げられないと仮定します(例えば、文字を保存するために ‘〜test_tx(1)’にそれを変更しません)
長さ)、
>この慣習は、コンピュータ上の他のすべてのプログラムによって認識され、受け入れられていることを前提としています( ‘test_txt’を含む名前を他の人が使用することはありません)。

そこで、私は別の解決策を書きました。その本質は次のとおりです。

>新しいXMLベースの.xlsxファイル(またはその他のOfficeファイル)を解凍します。
一時パスへのフォーマット(パスワードで保護されていない)。
> ‘/ xxx / embeddings’内のすべての.binファイルを繰り返し処理します( ‘xxx’ =
    ‘xl’または ‘word’または ‘ppt’)を入力し、.binを含む辞書を作成します。
    キーとしてのファイルの一時パスとから返される辞書
    値としてステップ3。
>に従って、.binファイルから情報を抽出します。
    よく文書化された)Ole Packager形式で、情報を
    辞書。 (生のバイナリデータを ‘contents’として取得します。
    .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

あなたはこのようにそれを使うことができます:

objects = read_zipped_xml_bin_embeddings( dir_path + '\\test_excel.xlsx' )
obj = objects.values()[ 0 ] # Get first element, or iterate somehow, the keys are the temporary paths
print( 'Original filename: ' + obj[ 'original_filename' ] )
print( 'Original filepath: ' + obj[ 'original_filepath' ] )
print( 'Original filepath: ' + obj[ 'temporary_filepath' ] )
print( 'Contents: ' + obj[ 'contents' ] )

転載記事の出典を記入してください: Python:クリップボードなしでOffice / Excelドキュメントから埋め込みOLEにアクセス - コードログ