2015/04/08

Python 2.7 + win32com で Excel ファイルを XML に変換

[Python][EXCEL][XML]
実行環境は Windows 7 32bit + Excel 2007 の PC で、ゲストユーザとしてログオン、一昨日セットアップした QGIS ポータブル版にある Python 2.7 + win32com パッケージをそのまま使用。以前は Windows XP + PL/Python3 で同様のことをしていましたが(前ブログでの記事)ポータブルな Python 単独でできるこちらの方が使いやすいです。

コードと使用例は後掲します。先に変換前・変換後の Excel と XML の例。↓ こんな風にデータが A1 から始まっていないとか複数の表が適当に置かれている変な Excel を時々もらうので、ともかくセル位置とデータだけ XML 形式で吸い出し、後は PostgreSQL で処理しています。(下の変換前後で数値が違うのは、セルが値でなく rand 関数で、開くたびに値が変わるから)



以下、使うまでの準備について。一昨日セットアップした QGIS ポータブル版のフォルダ直下に、自作 Python スクリプト用フォルダと、スクリプト起動用バッチファイルを置きます。↓


■ excel2xml.py.bat
@echo off
%~d0
cd %~p0
cd QGIS/bin
set PYTHONHOME=../apps/Python27
set PYTHONPATH=../apps/Python27/Lib
set PATH=%PYTHONHOME%
python "../../python_script/%~n0" "%1"
pause


↓ バッチファイルが呼び出す Python スクリプトを python_script フォルダ内に作成します。全て ASCII なのでエンコーディング指定は省略。ほとんど前ブログで書いた PL/Python3 のストアド関数と同じですが、単一ファイルの処理に特化し、文字列の扱いを Python 2 用に直しました。ファイルはこちら。

■ excel2xml.py
import os
import sys
import win32com.client
import codecs

fln = sys.argv[1].replace(os.path.sep, '/')
xml = fln + '.xml'

eao = win32com.client.Dispatch('Excel.Application')
eao.Visible = False
eao.DisplayAlerts = False

dic = []
wbo = eao.Workbooks.Open(fln)
for i in range(0, wbo.WorkSheets.Count) :
wso = wbo.WorkSheets(i + 1)
usr = wso.UsedRange
tags = []
for j in wso.Comments :
t = unicode(j.Text())
if not t : continue
tag = ['<comment r="', str(j.Parent.Row), '"']
tag.extend([' c="', str(j.Parent.Column), '">'])
tag.extend([t, '</comment>'])
tags.append(''.join(tag))
tmp = {'wsname' : wso.Name}
tmp['r_top'] = usr.Row
tmp['c_top'] = usr.Column
tmp['data'] = usr.Value
if tmp['data'] is None : continue
if len(tags) > 0 : tmp['comm'] = '\n'.join(tags)
dic.append(tmp)
if i == wbo.WorkSheets.Count : break
wbo.Close()
eao.Quit()

tags = [unicode('<b path="' + fln + '">')]
for w in dic :
tags.append(unicode('<s name="' + w['wsname'] + '">'))
if 'comm' in w : tags.append(w['comm'])
nrow = w['r_top']
for r in w['data'] :
ncol = w['c_top']
for c in r :
if c is not None :
tag = ['<w r="', str(nrow), '"']
tag.extend([' c="', str(ncol), '">'])
tag.extend([unicode(c), '</w>'])
tags.append(''.join(tag))
ncol = ncol + 1
nrow = nrow + 1
tags.append('</s>')
tags.append('</b>')
output = '\n'.join(tags)

f = codecs.open(xml, 'w', 'sjis')
f.write(output)
f.close
print output


使い方は単純で、バッチの引数に Excel ファイルのフルパスを渡すか、↓ のようにドラッグ & ドロップするだけ。複数渡された場合、最初のファイルだけ処理します。


↓ 正常に処理できれば、結果を表示し、Excel ファイルと同じ場所に XML を出力します。



冒頭の変換後は、この XML ファイルの中身です。ただし文字コードが Shift_JIS で、先頭にエンコーディング指定を含む XML 宣言がないので(複数の結果をつなぐ場合に備え)、ブラウザ等でそのままパースさせるとエラーになります。適宜 UTF-8 等で保存し直すかスクリプトを修正して下さい。あと、渡す Excel ファイルのパス・ファイル名に半角空白や日本語があると動きません。

↓ セルに付いたコメントまで取り込む必要は余りなさそうですが、念のため対応。セル位置とコメント文を一つの comment 要素にします。



システムにインストールされている Excel を win32com で起動しているわけですが、なぜか Python の対話モード(インタラクティブなシェル)で同じことをすると Excel の終了だけができず、いわゆるゾンビプロセスが発生。スクリプト化すると普通に終了でき、謎です。

これまで PL/Python3 しか使ったことがなく、Python 2 での日本語やファイルパスの処理で少し嵌まり、下記の情報にお世話になりました。m(_)m

■ Pythonの日本語処理
■ Python : コマンドライン引数の取得
■ Windowsではos.path.sepでよくはまる
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。