OpenPyXLでExcelファイルを読み取る
前回まではpandasを使ってExcelシートの読み書きを行いました。
参照
PandasでExcelファイルを読む - よちよちpython
PandasでExcelファイルに書き込む - よちよちpython
Pandasを使うとExcelシートを丸ごと読み書きするのに便利でした。
今回はOpenPyXL
モジュールを使ってExcelファイルの読み取りを行ってみようと思います。
OpenPyXLモジュールは、前回のpandasを使ってのExcel書き込みでインストールしたものと同じものです。
Python入門書でのExcelファイルの操作は大体このOpenPyXLモジュールを使ったものが載っているかと思います。
作業環境
PC
- Windows10
- Anaconda
- Jupyter Notebook
- python3.7
準備
モジュールのインストール確認
import openpyxl
を実行して、ModuleNotFoundError
が出なければインストール済み。
まだインストールがお済みでなかったら
$ pip install openpyxl
これで準備完了です。
参考
OpenPyXL公式
# 目次
- 作業環境
- 準備
- 注意点
- openpyxlでExcelを読む
注意点
この投稿は、Al Sweigart『退屈なことはPythonにやらせよう』オライリージャパン 2017年11月10日 初版第4刷発行 を参考に書いています。
2019年8月現在で最新版のOpenPyXLモジュールを使いますと、その改変等により書籍掲載のコードをそのまま実行するとエラーが出た箇所がいくつかありました。
(変数wb は Workbookオブジェクト)
- シート名取得 : wb.get_sheet_names() → 非推奨 wb.sheetnames に変更
- シート名からシート取得 : wb.get_sheet_by_name(シート名) → 非推奨 wb[シート名] に変更
- 特定列の取得 : sheet.columns[列インデックス番号] → list(sheet.columns)[列リストインデックス番号]
など。詳しいことはこちらで。
OpenPyXL公式
openpyxlでExcelを読む
カレントディレクトリに画像のような3行のデータが入った2シートのExcelファイルが保存してあります。
カレントディレクトリとExcelファイル名取得
カレントディレクトリの取得と、その中に保存してあるExcelファイル(.xlsx)のファイル名をリストで取得。
import os import glob # カレントディレクトリの取得 print(os.getcwd()) # カレントディレクトリ内のExcelファイル名(.xlsx)をリストで全取得 print(glob.glob("./*.xlsx"))
Excelのブックを読み込み
まずは openpyxl.load_workbook
でワークブックのオブジェクトを変数 wb に格納します。
import openpyxl # Excelファイルを読み込む wb = openpyxl.load_workbook('sample.xlsx') # そのタイプを表示 print(type(wb))
<class 'openpyxl.workbook.workbook.Workbook'>
シート名取得
上で格納したワークブックのオブジェクトから get_sheet_names()
でシート名を取り出します。
sheet_name_lst = wb.get_sheet_names()
print(sheet_name_lst)
上記を実行すると、シート名のリストが['Sheet1', 'Sheet2']
とちゃんと取得されて表示されましたが、
そのあとに以下のような注意が表示されました。
C:\Users\ユーザー名\Anaconda3\lib\site-packages\ipykernel_launcher.py:1: DeprecationWarning: Call to deprecated function get_sheet_names (Use wb.sheetnames). """Entry point for launching an IPython kernel.
「DeprecationWarning:」 は非推奨の警告
get_sheet_names
は非推奨。wb.sheetnames を使いなさい とのことらしい。
了解しました!
下のように書き換えます。
sheet_name_lst = wb.sheetnames
print(sheet_name_lst)
['Sheet1', 'Sheet2']
今度はエラーなしでシート名のリストが取得できました。
上の wb.sheetnames
の部分を wb.sheetnames()
とカッコつけて書くと
TypeError: 'list' object is not callable
エラーが出ます。「無駄にカッコつけやがって・・・」
カッコ無しで wb.sheetnames
シート取得
get_sheet_by_name()
メソッドにシート名を渡すとシートのオブジェクトが取得できます。
上で取得したシート名のうちの Sheet1
を入れてみます。
sheet = wb.get_sheet_by_name("Sheet1") print(sheet)
実行したら、またまた「DeprecationWarning:」が出ました。
DeprecationWarning: Call to deprecated function get_sheet_by_name (Use wb[sheetname]). """Entry point for launching an IPython kernel.
get_sheet_by_name
は非推奨なので wb[sheetname]
を使ってねと。
ラジャー!
sheet = wb["Sheet1"] print(sheet)
<Worksheet "Sheet1">
シートからセルを取得
いま変数「sheet」にSheet1のオブジェクトが入っています。
Sheet1の A1
のセルのオブジェクトを取得します。
sheet['A1']
<Cell 'Sheet1'.A1>
セルの値を取得する
「A1」セルのオブジェクトから具体的な値を value
で取り出します。
sheet['A1'].value
1
シートの最終行を取得
sheet.max_row
3
シートの最終列を取得
sheet.max_column
7
列の数字を文字に変換
Sheet1には3行7列に値が入っている事が分かりました。
7列目が何の文字にあたるか分かるように変換します。
文字に変換する場合は openpyxl.utils.get_column_letter
関数を使います。
openpyxl.utils.get_column_letter(7)
'G'
列の文字を数字に変換
列の文字をに変換する場合は openpyxl.utils.column_index_from_string
関数を使います。
列番号「AX」が何列目かを数字に変換して確かめてみます。
openpyxl.utils.column_index_from_string("AX")
50
特定行のオブジェクトを取得
sheet.rows
<generator object Worksheet._cells_by_row at 0x000001E04DAC9F48>
特定行オブジェクトをfor文で取得
for i in list(sheet.rows)[0]: print(i)
<Cell 'Sheet2'.A1>
<Cell 'Sheet2'.B1>
<Cell 'Sheet2'.C1>
<Cell 'Sheet2'.D1>
<Cell 'Sheet2'.E1>
<Cell 'Sheet2'.F1>
<Cell 'Sheet2'.G1>
上のコードのlist(sheet.rows)[0]
でインデックス番号を指定しなければ、値の入っている全ての行ごとにリストで取り出せます。
for i in list(sheet.rows): print(i)
(<Cell 'Sheet2'.A1>, <Cell 'Sheet2'.B1>, <Cell 'Sheet2'.C1>, <Cell 'Sheet2'.D1>, <Cell 'Sheet2'.E1>, <Cell 'Sheet2'.F1>, <Cell 'Sheet2'.G1>)
(<Cell 'Sheet2'.A2>, <Cell 'Sheet2'.B2>, <Cell 'Sheet2'.C2>, <Cell 'Sheet2'.D2>, <Cell 'Sheet2'.E2>, <Cell 'Sheet2'.F2>, <Cell 'Sheet2'.G2>)
(<Cell 'Sheet2'.A3>, <Cell 'Sheet2'.B3>, <Cell 'Sheet2'.C3>, <Cell 'Sheet2'.D3>, <Cell 'Sheet2'.E3>, <Cell 'Sheet2'.F3>, <Cell 'Sheet2'.G3>)
特定行オブジェクトから列番号を指定して値を取得
for i in list(sheet.rows)[2]: print(i.value)
3
多々財閥の歴史
印録出版
ABCブック
2540
2
2
取得できた行の3列目(C列)のセルが取得できた
行オブジェクトから列番号を指定してそれぞれの値を取得
3行目(index 2)の値が取得できた。
列のオブジェクトを取得
index=0 1列目を取得。
sheet.columns[0]
TypeError: 'generator' object is not subscriptable
上記のエラーが出ます。[ ]
を外して使います。
sheet.columns
<generator object Worksheet._cells_by_col at 0x000001E04DCCB228>
列のオブジェクトをリストで取得
列ごとにリストで取得。
list(sheet.columns)
[(<Cell 'Sheet2'.A1>, <Cell 'Sheet2'.A2>, <Cell 'Sheet2'.A3>),
(<Cell 'Sheet2'.B1>, <Cell 'Sheet2'.B2>, <Cell 'Sheet2'.B3>),
(<Cell 'Sheet2'.C1>, <Cell 'Sheet2'.C2>, <Cell 'Sheet2'.C3>),
(<Cell 'Sheet2'.D1>, <Cell 'Sheet2'.D2>, <Cell 'Sheet2'.D3>),
(<Cell 'Sheet2'.E1>, <Cell 'Sheet2'.E2>, <Cell 'Sheet2'.E3>),
(<Cell 'Sheet2'.F1>, <Cell 'Sheet2'.F2>, <Cell 'Sheet2'.F3>),
(<Cell 'Sheet2'.G1>, <Cell 'Sheet2'.G2>, <Cell 'Sheet2'.G3>)]
列オブジェクトのリストからインデックスで取得
list(sheet.columns)[1]
(<Cell 'Sheet2'.B1>, <Cell 'Sheet2'.B2>, <Cell 'Sheet2'.B3>)
indexが1(2列目 = B列目)が取得できた。
列オブジェクトのリストからインデックス指定で値を取得
for i in list(sheet.columns)[1]: print(i.value)
せせらぎの歌
そそがる君
多々財閥の歴史
行と列を数字で指定してセルのオブジェクトを取得
sheet.cell(row = 1, column = 2)
<Cell 'Sheet1'.B1>
行と列を数字で指定してセルの値を取得
sheet.cell(row = 3, column = 2).value
'スースー冷却枕'
複数の行と列を指定してセルのオブジェクトを取得
sheet["A1":"G3"]
((<Cell 'Sheet1'.A1>,
<Cell 'Sheet1'.B1>,
<Cell 'Sheet1'.C1>,
<Cell 'Sheet1'.D1>,
<Cell 'Sheet1'.E1>,
<Cell 'Sheet1'.F1>,
<Cell 'Sheet1'.G1>),
(<Cell 'Sheet1'.A2>,
<Cell 'Sheet1'.B2>,
<Cell 'Sheet1'.C2>,
<Cell 'Sheet1'.D2>,
<Cell 'Sheet1'.E2>,
<Cell 'Sheet1'.F2>,
<Cell 'Sheet1'.G2>),
(<Cell 'Sheet1'.A3>,
<Cell 'Sheet1'.B3>,
<Cell 'Sheet1'.C3>,
<Cell 'Sheet1'.D3>,
<Cell 'Sheet1'.E3>,
<Cell 'Sheet1'.F3>,
<Cell 'Sheet1'.G3>))
複数の行と列を指定してセルの値を取得
複数のセルの値を取り出すには、一旦行のオブジェクトをfor文で取り出し、さらにそこからセルの値を取り出します。
for row_obj in sheet["A1":"G3"]: for cell_obj in row_obj: print(cell_obj.value) print("=======")
1
ささパンダ
株式会社竹富
いかした商事
250
50
1
=======
2
しーしーオマル
(有)白鳥
(有)白鳥
8500
8
3
=======
3
スースー冷却枕
北枕社
仏霊園
980
45
3
=======
どこのセルに入っているか分かりやすいように cell_obj.coordinate
でセル番号を表示させます。
for row_obj in sheet["A1":"G3"]: for cell_obj in row_obj: print(cell_obj.coordinate,cell_obj.value) print("=======")
A1 1
B1 ささパンダ
C1 株式会社竹富
D1 いかした商事
E1 250
F1 50
G1 1
=======
A2 2
B2 しーしーオマル
C2 (有)白鳥
D2 (有)白鳥
E2 8500
F2 8
G2 3
=======
A3 3
B3 スースー冷却枕
C3 北枕社
D3 仏霊園
E3 980
F3 45
G3 3
=======