openpyxl:範囲指定をせずにExcelからデータを取得する方法

※社外のコラムニストによる記事です。Python関連の情報をお伝えします。
「シゴトがはかどるPython自動処理の教科書(著:クジラ飛行机様/マイナビ出版)」を利用して、Pythonを使った自動化について学んでいきます。
前回、Chapter2-6「Excelシートのデータ抽出テクニック」に入り、売上データが保存されているファイルからデータを取得する方法を学びました。
今回は、範囲を指定せずに、Excelファイルに保存されているデータを取得する方法について学びます。
目次
Excelファイルから売上データをすべて取り出してみる
前回、売上データを取得する際、範囲指定をしていました。

<サンプルコードの一部分>
# A3からF9のセルを取り出す --- (*1)
rows = sheet["A3":"F9"]
for row in rows:
# セルの値をリストとして得る --- (*2)
values = [cell.value for cell in row]
# リストを表示する --- (*3)
print(values)
とはいえ、売上データのように、毎回範囲が必ず同じというものばかりではないと思いますので、範囲を指定する際は内容を確認して調べる必要があります。実際にファイルを開いて確認すれば済む話ですが、プログラム内に含めることができるそうです。
まずはデータを取得する方法と、その注意点についてです。
P.73
ワークシートには最下行を示すmax_row、最右列を示すmax_columnというプロパティがあるので、これを利用することで最下行を調べることができます。
ただし、このmax_rowとmax_columnには弱点があります。セルに罫線が入っている場合、罫線が有効な範囲を指し示してしまいます。
(中略)
ただし、罫線を引く際に列のヘッダを選択して罫線を引いた場合でも、max_rowは罫線に関係なく正確な最下行を示します。見た目には分からないのですが、どのように罫線を引いたかによって、max_rowの値が変化してしまうのが難しいところです。 そのため、解決策としては、とりあえずmax_rowが指し示す範囲のデータを取り出して見て、その後でデータが空かどうかを調べるといった処理を行うと良いでしょう。
なんと、openpyxlがExcelのセルが使用されていると判断する基準は、データや数式が入っているときだけでなく、罫線も対象になるということです…。ちなみに、背景色やフォントを設定した場合も「使用済み」と判定するようです。
既存ファイルで複数人が触れるものを対象としたときを考えると、あずかり知らぬ書式設定がされている可能性が出てくるので、対応策を知っておく必要がありますね…。
対応策としては、
- 大きな範囲を取得して、セルの値がNoneか調べる
- iter_rowsメソッドで引数max_rowを省略する(=繰り返し行を読んでくれる)
という方法があるようです。それぞれの方法を見ていきます。
1)大きな範囲を取得して、セルの値がNoneか調べる
サンプルコードがこちら。
import openpyxl as excel
# 売上データのブックを開いてシートを取り出す
book = excel.load_workbook(
"uriage.xlsx", data_only=True)
sheet = book.active
# A3からF999(データの適当な範囲)を取り出す --- (*1)
rows = sheet["A3":"F999"]
for row in rows:
# セルの値をリストとして得る --- (*2)
values = [cell.value for cell in row]
# 空白セルであれば読み取りを終わる --- (*3)
if values[0] is None: break
# リストを表示する
print(values)
変わったのは範囲の指定部分と、if~が追加されているところですね。
これを実際に実行するとこうなります。(Excelファイルの罫線は15行目まで伸ばしました。)

問題なく表示されました。
2)iter_rowsメソッドで引数max_rowを省略する
サンプルコードがこちら。
iter_rowsメソッドを使うと全てのセルを取り出せるものになりますが、このサンプルではA3以降のデータをとりだすことになるので、開始行としてmin_rowの値を3で設定します。
import openpyxl as excel
# 売上データのブックを開いてシートを取り出す
sheet = excel.load_workbook(
"uriage.xlsx", data_only=True).active
# iter_rowsを使って全データを取り出す --- (*1)
for row in sheet.iter_rows(min_row=3):
values = [cell.value for cell in row]
if values[0] is None: break
print(values)
こちらも実行してみると先ほどと同じ結果が表示されました。
逆に引数(min_row=3)を省略した場合はすべての行をとりだしてくれるので、結果はこうなります。

タイトルから取得してくれていることが分かりますね。
また、列も指定したい場合は、min_rowだけでなく、min_colを追加してあげます。
例)B3→min_row=3, min_col=2

それではきりが良いのでこちらで終了です。今回もお付き合いいただき、ありがとうございました。
Pythonの自動化で業務の効率化を図りたい方は、グローバルウェイに依頼してみてはいかがでしょうか?興味がある方は以下をご覧の上、是非お問い合わせください。