python openpyxl でエクセルを読み書きする

python openpyxl でエクセルを読み書きする

2024/01/15 00:00:00
Program
Python

前提 #

準備 #

python 3.11.7 の開発環境構築

$ mkdir python-openpyxl
$ cd python-openpyxl
$ pyenv local 3.11.7
$ python -m venv venv
$ source venv/bin/activate
$ pip install openpyxl
...
[notice] A new release of pip is available: 23.2.1 -> 23.3.2
[notice] To update, run: pip install --upgrade pip
# pip 更新の通知がでれば更新しておく
$ pip install --upgrade pip
# requirements.txt を作成しておく
# 作成済みの場合、 $ pip install -r requirements.txt でパッケージインストール可能
$ pip freeze > requirements.txt

# エディタで静的解析が必要な場合(poetry使わないと開発用パッケージが混ざるので注意)
$ pip install jedi flake8 importmagic autopep8 yapf black

書き込み #

以下のexcelファイルを作成するために必要最低限の機能を含んだサンプルを示す。

サンプルソースは以下の内容を示している

  • サンプルアプリ実行時の入力引数の解析
  • excel の座標系変換(実験)
  • book を新規作成
  • book 作成時に勝手に作成されるSheetを削除
  • sheet(‘オフィス’) を作成
  • cell に情報代入
    • 背景色
    • 罫線
    • マージ
  • book をファイル名を指定して保存
  • book を閉じる

$ emacs write_xls.py

 1import openpyxl
 2from openpyxl.utils.cell import coordinate_from_string
 3from openpyxl.utils import column_index_from_string, get_column_letter
 4from openpyxl.styles.borders import Border, Side
 5from openpyxl.styles import PatternFill
 6import argparse
 7import traceback
 8
 9
10def get_excel_coordinates(row, column):
11    '''
12    row,col から excel 座標を返す
13    ex: (1, 1) = A1
14    '''
15    return get_column_letter(column) + str(row)
16
17
18def get_row_col(excel_pos):
19    '''
20    excel 座標からrow,col位置を返す
21    ex: A1 = (1, 1)
22    '''
23    col_letter, row = coordinate_from_string(excel_pos)  # 位置取得
24    col = column_index_from_string(col_letter)
25    return row, col
26
27
28def create_border(top=False, bottom=False, left=False, right=False):
29    '''
30    セルに罫線を引く
31    '''
32    side = Side(style='thin', color='000000')  # 罫線のスタイルと色を設定
33    border = Border(
34        top=side if top else None,
35        bottom=side if bottom else None,
36        left=side if left else None,
37        right=side if right else None
38    )  # 罫線を設定
39    return border
40
41
42def main(args):
43    # excel 座標系変換実験
44    print("(2, 2)={}".format(get_excel_coordinates(2, 2)))  # row=2, col=2 をexcel座標系に変換すると B2
45    print("(B2)={}".format(get_row_col('B2')))  # B2 を row, col に変換すると (2, 2)
46    wb = openpyxl.Workbook()  # book 新規作成(勝手にゼロ番目のシートにシート名「Sheet」が作成されるので注意)
47    wb.remove(wb["Sheet"])  # 勝手作成されたSheetを削除
48    ws = wb.create_sheet(title='オフィス')
49    # 本スクリプトは utf8-lf のため、文字コードを意識してexcelに代入する必要はない
50    items = [
51        {'name': 'word', 'name-jp': 'ワード'},
52        {'name': 'excel', 'name-jp': 'エクセル'},
53        {'name': 'powerpoint', 'name-jp': 'パワポ'},
54        {'name': 'pdf', 'name-jp': 'ピーディーエフ'},
55    ]
56    ws['A1'].value = '書き込みサンプル'  # excel 座標系 A1 に '書き込みサンプル' を書き込み
57    for index in range(len(items)):
58        item = items[index]  # 書き込むitem情報
59        border = create_border(top=True, bottom=True, left=True, right=True)  # 4方向すべて罫線を引く
60        bg_gray = PatternFill(patternType="solid", fgColor="a0a0a0")  # 塗りつぶし色グレー
61        # 以下の書き込みはrow,col形式で座標を設定している。row,colが (1, 1) の場合、 excel座標系は A1 となる
62        row = index + 2  # row は1始まりでかつ、A1 にはすでに記載があるので +2
63        # 番号を書き込み
64        cell = ws.cell(row, 1)
65        cell.value = str(index + 1)
66        cell.border = border
67        cell.fill = bg_gray  # 背景色をグレーにする
68        # name-jp を書き込み
69        cell = ws.cell(row, 1+1)
70        cell.value = str(item['name-jp'])
71        cell.border = border  # 罫線設定
72        ws.merge_cells(start_row=row, start_column=1+1,  # セルマージ:開始row,col
73                       end_row=row, end_column=1+2)  # セルマージ:終了row,col
74        # name を書き込み
75        cell = ws.cell(row, 1+3)
76        cell.value = str(item['name'])
77        cell.border = border  # 罫線設定
78    wb.save(args.xlsfile)  # excel ファイル書き出し(入力引数1番)
79    wb.close()  # book 閉じる
80    print("complete")
81
82
83if __name__ == '__main__':  # プログラム実行ポイント
84    try:
85        # 入力引数設定
86        parser = argparse.ArgumentParser(description='xls読み込み')
87        parser.add_argument('xlsfile', help='xls ファイル')  # 入力引数1:excelファイルであることを示す
88        args = parser.parse_args()  # 入力引数取得
89        main(args)
90    except Exception:  # as e: # main() で発生する異常はすべてキャッチする
91        t = traceback.format_exc()
92        print("ERROR: {}".format(t))

実行

# test.xlsx は入力引数1番で、出力するexcelファイル名を示す
$ python write_xls.py test.xlsx
(2, 2)=B2    # row,col から excel 座標系確認ログ
(B2)=(2, 2)  # excel 座標系から row, col 確認ログ
complete

読み込み #

“書き込み"サンプルで作成した test.xlsx を読み込んで内容をテキスト表示するサンプルを示す。

サンプルソースは以下の内容を示している

  • book を開く
  • sheet を開く
  • cell から情報を取得
    • 背景色
    • 罫線
    • マージ
  • book を閉じる

$ emacs read_xls.py

  1import openpyxl
  2from openpyxl.utils import get_column_letter
  3import argparse
  4import traceback
  5
  6
  7def get_excel_coordinates(row, column):
  8    '''
  9    row,col から excel 座標を返す
 10    ex: (1, 1) = A1
 11    '''
 12    return get_column_letter(column) + str(row)
 13
 14
 15def get_borders(cell):
 16    '''
 17    セルの罫線の状態を確認する
 18    '''
 19    borders = []
 20    if cell.border.left.style:
 21        borders.append('LEFT')
 22    if cell.border.right.style:
 23        borders.append('RIGHT')
 24    if cell.border.top.style:
 25        borders.append('TOP')
 26    if cell.border.bottom.style:
 27        borders.append('BOTTOM')
 28    return borders
 29
 30
 31def get_bgcolor(cell):
 32    '''
 33    セルの背景色を取得
 34    '''
 35    bgcolor = cell.fill.start_color.index
 36    return bgcolor
 37
 38
 39def get_merged_cells(sheet, cell):
 40    '''
 41    セルのマージ情報を取得
 42    '''
 43    cell_index = cell.coordinate
 44    # シート内の全マージリストからcellに該当するものがあるか検索
 45    for range_ in sheet.merged_cells.ranges:
 46        # sheet.merged_cells.ranges = {<MergedCellRange B2:C2>, <MergedCellRange B3:C3>, ...}
 47        merged_cells = list(openpyxl.utils.rows_from_range(str(range_)))
 48        for row in merged_cells:  # (A1,A2,A3), (B1,B2,B3), ... セルのマージ座標
 49            if cell_index in row:
 50                # マージあり
 51                return merged_cells
 52    # マージなし
 53    return None
 54
 55
 56def main(args):
 57    # excel 座標系変換実験
 58    wb = openpyxl.load_workbook(args.xlsfile)
 59    print("wb.worksheets:{}".format(wb.worksheets))  # worksheetオブジェクトのリストを取得
 60    print("wb.sheetnames:{}".format(wb.sheetnames))  # シート名のリストを取得
 61    ws = wb["オフィス"]
 62    print("ws['A1']:{}".format(ws['A1'].value))  # A1 の内容を取得
 63    for index in range(100):  # とりあえず100row検索することにする
 64        row = index + 2  # row は1始まりでかつ、A1 にはすでに記載があるので +2
 65        if not ws.cell(row, 1).value:  # Noneの場合、番号なしなので終端とする
 66            break
 67        # 以下で、番号,name-jp,name のそれぞれのセル情報を出力する
 68        #  - value: セルの値
 69        #  - borders: 罫線情報
 70        #  - merged_cells: マージしている場合、マージしているセルリスト
 71        # 番号のセル情報を取得
 72        cell = ws.cell(row, 1)
 73        excel_pos = get_excel_coordinates(row, 1)
 74        bgcolor = get_bgcolor(cell)
 75        borders = get_borders(cell)
 76        merged_cells = get_merged_cells(ws, cell)
 77        print("number {}:value={},bgcolor={},borders={},merged_cells={}".format(excel_pos, cell.value, bgcolor, ",".join(borders), merged_cells))
 78        # name-jpのセル情報を取得
 79        cell = ws.cell(row, 2)
 80        excel_pos = get_excel_coordinates(row, 2)
 81        bgcolor = get_bgcolor(cell)
 82        borders = get_borders(cell)
 83        merged_cells = get_merged_cells(ws, cell)
 84        print("name-jp {}:value={},bgcolor={},borders={},merged_cells={}".format(excel_pos, cell.value, bgcolor, ",".join(borders), merged_cells))
 85        # nameのセル情報を取得
 86        cell = ws.cell(row, 4)
 87        excel_pos = get_excel_coordinates(row, 4)
 88        bgcolor = get_bgcolor(cell)
 89        borders = get_borders(cell)
 90        merged_cells = get_merged_cells(ws, cell)
 91        print("name {}:value={},bgcolor={},borders={},merged_cells={}".format(excel_pos, cell.value, bgcolor, ",".join(borders), merged_cells))
 92
 93    wb.close()  # book 閉じる
 94    print("complete")
 95
 96
 97if __name__ == '__main__':  # プログラム実行ポイント
 98    try:
 99        # 入力引数設定
100        parser = argparse.ArgumentParser(description='xls読み込み')
101        parser.add_argument('xlsfile', help='xls ファイル')  # 入力引数1:excelファイルであることを示す
102        args = parser.parse_args()  # 入力引数取得
103        main(args)
104    except Exception:  # as e: # main() で発生する異常はすべてキャッチする
105        t = traceback.format_exc()
106        print("ERROR: {}".format(t))

実行

# 入力引数1番は、作成したtest.xlsxを指定
$ python read_xls.py test.xlsx
wb.worksheets:[<Worksheet "オフィス">]
wb.sheetnames:['オフィス']
ws['A1']:書き込みサンプル
number A2:value=1,bgcolor=00a0a0a0,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
name-jp B2:value=ワード,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=[('B2', 'C2')]
name D2:value=word,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
number A3:value=2,bgcolor=00a0a0a0,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
name-jp B3:value=エクセル,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=[('B3', 'C3')]
name D3:value=excel,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
number A4:value=3,bgcolor=00a0a0a0,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
name-jp B4:value=パワポ,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=[('B4', 'C4')]
name D4:value=powerpoint,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
number A5:value=4,bgcolor=00a0a0a0,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
name-jp B5:value=ピーディーエフ,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=[('B5', 'C5')]
name D5:value=pdf,bgcolor=00000000,borders=LEFT,RIGHT,TOP,BOTTOM,merged_cells=None
complete

参考UR #