憂鬱なExcel作業をPythonで紛らわす

自分の組み込み業界ではやたらExcelが多くて、Excelドキュメントのレビューの機会が度々ある。その中には、ファイル間のトレーサビリティ目視チェックといった、時に刺身タンポポと揶揄されるような気の滅入る作業も少なくない。


こういった作業は、周知の通りだと思うけれど、マイクロソフト系の言語や、PythonRubyなど様々なプログラミング言語Excel操作のライブラリを提供しているおかげで、自動化できることが多い。
そのため基本姿勢として自動化に手を付けてみるのは良いと思う。生産性が上がることが多いのもある。また何より、例えば「Excelの目視レビューでなく、Pythonのコーディングをしている」と思えば気を紛らわせられる、ような気がする。ソフトウェア開発者として精神衛生的に良い。

今回はその自動化の実現手段の一つとして、Pythonのopenpyxlを使ったExcelドキュメントのチェックについて書きたい。

openpyxlの大まかな使い方

openpyxlはPythonExcelの読み書きを行うためのライブラリ。今回は読み取りのみ扱っているけれど、Excelファイルの生成や内容更新もできる。

インストール

インストールはpipで可能(pip install openpyxl実行)

ファイルのロードの内容の表示

openpyxl.load_workbookでファイル名を指定してワークブックを読み出し操作する。
例えばファイル名「仕様書2.xlsx」の、仕様項目定義シートのB3セルの値を表示する場合、以下のように記述する。

from openpyxl import load_workbook

wb = load_workbook(filename = "仕様書2.xlsx", read_only=True)
print(wb["仕様項目定義"]["b3"].value)


複数のセルを扱いたい場合は、以下のようにiter_rows等を使用する

from openpyxl import load_workbook

wb = load_workbook(filename = "仕様書2.xlsx", read_only=True)
#仕様項目定義シートのB1からB20までのセルの値を表示
for id_area in wb["仕様項目定義"].iter_rows("B1:B20"):
    for data in id_area:
        print(data.value)

全シートを参照する場合は、ワークブックオブジェクトのworksheetsに対してfor inループを回す。

from openpyxl import load_workbook
wb = load_workbook(filename = "仕様書2.xlsx", read_only=True)
#全シートのタイトルとA1セルの値を表示
for sheet in wb:
    print(sheet.title)
    print(sheet["A1"].value)

サンプル

複数間のファイルでIDの整合性がとれるかという、よくありがちで憂鬱なチェックをopenpyxlで行う。
具体的には、「仕様書2.xlsx」と「トレーサビリティマトリクス.xlsx」の仕様項目IDが一致しているかを確認する。

仕様項目2.xlsx 仕様項目定義シート
●仕様項目定義
ID 仕様項目詳細
SPEC1 AAA
SPEC2 BBB
SPEC13 CCC
SPEC4 DDD
SPEC5 EEE
SPEC6 FFF
トレーサビリティマトリクス.xlsx トレーサビリティマトリクスシート
仕様項目
SPEC1 SPEC2 SPEC3
要件 REQ1
REQ2
REQ3

確認コード

# coding: shift_jis
from openpyxl import load_workbook

if __name__ == '__main__':

    # トレーサビリティマトリクスから仕様項目IDを抽出
    wb_tm = load_workbook(filename = "トレーサビリティマトリクス.xlsx", read_only=True)
    
    tm_id_list = []
    for id_area in wb_tm["トレーサビリティマトリクス"].iter_rows('D4:AO4'):
        for data in id_area:
            #最初の文字列から空欄までの文字列をピックアップ
            if data.value != "" and data.value != None:
                tm_id_list.append(data.value)
            if data.value == "" and len(tm_id_list) > 0:
                break


    # 仕様書から仕様項目IDを抽出
    wb = load_workbook(filename = "仕様書2.xlsx", read_only=True)

    spec_id_list = []
    in_idarea = False
    for id_area in wb["仕様項目定義"].iter_rows("B1:B500"):
        for data in id_area:
            # タイトル"●仕様項目定義"から空欄までの文字列をピックアップ
            if in_idarea:
                if data.value == "":
                    break;
                if data.value != "ID":
                    spec_id_list.append(data.value)
                        
            if data.value =="●仕様項目定義":
                # 仕様項目IDの記述欄開始
                in_idarea = True
    
    
    if sorted(spec_id_list) == sorted(tm_id_list):
        print("整合")
    else:
        print("不整合")