よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

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公式


# 目次


注意点

この投稿は、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を読む

f:id:chayarokurokuro:20190824211424j:plain

カレントディレクトリに画像のような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
=======