seraphyの日記

日記というよりは過去を振り返るときのための単なる備忘録

Python2.5でExcelを使わずにExcelファイルを読み書きする。

動機

上記JAVAを試したから、というわけではなくて、むしろ、こちらが目的。
Pythonのような軽量言語ではゴリゴリ作りこむよりも、利用できるものは利用してしまったほうがよいと思う。そこでExcelを使って入力データを作ってからPythonで読み込ませたり、あるいはPythonで集計した結果をExcelで出力できたら良さそうだな、というところが動機。
ファイル形式としてはCSVファイルでも良かったりするのだが、これは案外難しいし、Excelの複数シートの扱いや、出力するとしても色づけ、罫線づけは魅力的だと思う。

準備

PythonExcelファイルを扱うためのライブラリとして、pyExceleratorというものがあるらしい。これを使うと、Excelの読み書きが簡単にできる。
http://sourceforge.net/projects/pyexcelerator/
ライセンスはBSD Lisenceである。

  1. ダウンロードページから、pyExcelerator-xxx.zipを取得する。
  2. これを展開し、setupを実行する。
    python setup.py install

これでpyExceleratorがインストールされる。

Excelファイルを作成してみる。

ExcelファイルをExcelを使わずにPythonから作成してみるテスト。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyExcelerator

# スタイルのキャッシュ
styles = {};

# パターン番号からスタイルを生成しキャッシュする.
# 同じスタイルとなる場合は生成済みのものを使用する.
# (Excel97-2003形式で使えるスタイル数は4000個までのため
# 同じスタイルの場合は新しいスタイルを作らないことで節約する.)
# http://office.microsoft.com/ja-jp/excel-help/HA010077823.aspx
def findOrCreateStyle(cnt):
    # パターン番号から、カラーと罫線の組み合わせキーを作成する.
    # (見た目が段違いになるように割り切れないようにしておく.)
    color = cnt % 9
    border = cnt % 3
    key = (color, border)

    # キーに対するスタイルが未作成の場合のみスタイルを作成する.
    if key not in styles:
        style = pyExcelerator.XFStyle()
        style.num_format_str = "@" # 文字列フォーマット
        
        # セルの水平・垂直揃えを中央にする
        alignment = pyExcelerator.Formatting.Alignment()
        alignment.horz = pyExcelerator.Formatting.Alignment.HORZ_CENTER
        alignment.vert = pyExcelerator.Formatting.Alignment.VERT_CENTER
        style.alignment = alignment
        
        # 罫線をつける
        if border in (1, 2):
            borders = pyExcelerator.Formatting.Borders()
            if border == 1:
                borders.bottom = pyExcelerator.Formatting.Borders.MEDIUM
            if border == 2:
                borders.left = pyExcelerator.Formatting.Borders.THIN
            style.borders = borders
        
        font = pyExcelerator.Formatting.Font()
        # 定義色: 0黒 1白 2赤 3緑 4青 5黄 6マゼンダ 7シアン
        # 詳細はBIFFRecords.py 参照
        font.colour_index = color % 8
        font.bold = True # 太字
        font.height = 16 * 20 # 16pt (1/20pt単位で指定する)
        style.font = font
        
        # スタイルの保存
        styles[key] = style

    # 作成済みスタイルを返す.
    return styles[key]

def main():
    # 新しいブックをメモリ上に作成する
    workbook = pyExcelerator.Workbook()

    cnt = 0

    # 新しいシートを作成する。
    for sheetNo in range(0, 10):
        # UNICODEでシートタイトル指定
        worksheet = workbook.add_sheet(u"シート%d" % sheetNo)

        # 行 x 列 でセルを埋める
        for row in range(0, 10): # 1行目は0
            for col in range(0, 20): # A列は0
                # スタイルを設定
                style = findOrCreateStyle(cnt)
                cnt += 1

                # セルに書き込み
                worksheet.write(row, col,
                                label = u"%d表%d行%d列" % (sheetNo, row, col),
                                style = style)

        # 列幅を設定する
        for col in range(0, 20):
            # 15文字幅 (デフォルトのフォントで文字「0」の1/256を単位とする)
            worksheet.col(col).width = 256 * 15

    # ブックをファイルに保存する。(暗黙で上書き保存)
    workbook.save(u"テスト1.xls")

# 実行
main()

(ソースコードは、こちら)

驚くことでもないのかもしれないが、Unicode文字列として指定さえすれば文字化けもすることなく日本語が普通に扱える。
しかも、JavaのPOIで書くよりもシンプルで簡単なようである。
このpyExceleratorのプロジェクトには、まともなドキュメントのようなものがないようなのだが(さがしてもみつからない)、サンプルを見れば、使い方はだいたい分かるだろう。
それでも分からなければソースを見れば良い。丁寧なコメントがついていて、ついでにExcelのBIFFフォーマットにもちょっと詳しくなれるかもしれない特典付きである。

とりあえず、Excelファイルを書くのは、とても簡単だという印象。

Excelファイルを読み込んでみる

ExcelファイルをExcelを使わずにPythonから読み込んでみるテスト。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyExcelerator

def main():
    sheets = pyExcelerator.parse_xls(u"テスト1.xls")
    for (sheetName, sheet) in sheets:
        print u"シート名: %s" % sheetName
        
        # シートは有効なカラムをあらわす(行・列)のタプルをキーとするマップとして扱われる。
        # よって、まず、有効なカラム位置を取得し、行列順に並び替える。
        keys = sorted(sheet.keys())
        
        for (row, col) in keys:
            value = sheet[(row, col)]
            print u"%2d行%2d列: %s" % (row, col, value)
        
# 実行
main()

これでExcelの全シート、全セルの値を標準出力に送り出せる。
サンプルコードではcp???という具合にキャラクターセットを指定していたが、省略しても問題なく日本語が読み込めた。(UTF-16扱いなことが幸いしたのであろうか。)
すごい短いことになっているが、だが、喜ぶのは早計である。
見てのとおり、Excelの書き方と読み方はぜんぜん違う。
書き方では、一応、シートがあって行があってセルがあって…、というオブジェクトの体裁をもっているのだが、読み込みのほうはシート名、セル位置を表すタプルとセルデータのスパース配列*1のペアとして「解析済み」の形で取得される。とりあえず、セルのデータを読む上では不自由ない。(だから、私的には問題ないのだが。)

だが、Excelファイルを開いて編集して書き戻す、といったRoundtripな使い方は無理っぽいのである。

とはいえ、解析済みで「スパース配列」として戻ってくるところはうれしいかもしれない。むしろ、余計な手間もなく簡単に目的のコードがかけそうだからである。

結論

POIと比べると柔軟性や出来ることは限定されてしまうが、データの読み書きとして使う分には実用的であると思える。
Pythonでも非常に簡単にExcelファイルの読み書きができることがわかった。
このpyExceleratorは是非活用すべきライブラリのように思う。

*1:シートのセルは飛び地でもかまわないが、その場合、そのセル間の隙間はデータとして存在していない。pyExceleratorのExcelファイルの読み込みでは、埋まっているセルだけが戻るので、有効なセルであるかの判断やセルがどこまであるのかといったチェックも不要である。