python openpyxl でエクセルを読み書きする
2024/01/15 00:00:00
前提 #
- 開発環境は wsl2 ubuntu20.04
構築手順は以下を参照
https://blog.oya3.net/docs/ubuntu/22.04/ubuntu-settings/ - 本内容で作成したサンプルソース一式
https://github.com/oya3/pyexcel
準備 #
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 #
- openpyxlでよく使う操作についてのまとめ
https://qiita.com/YukiYamam/items/d0e38921f5b51a950c40 - [解決!Python]OpenPyXLを使ってExcelファイルを読み書きするには
https://atmarkit.itmedia.co.jp/ait/articles/2202/08/news031.html - openpyxlでExcelを操作する【Python入門】
https://tech-blog.rakus.co.jp/entry/20210729/openpyxl