[openpyxl]PythonでExcelファイルのデータを処理する

## はじめに
Excelファイルに入力されたデータを抽出して色々処理する必要があったので、PythonでExcelファイルを操作できるモジュール`openpyxl`の使い方についての簡単なメモ

## 準備など
* 動作環境
  * Python 3.6.1
  * openpyxl 3.0.5

普通に`pip`コマンドでインストールします

```
pip install openpyxl
```

## データの読み書き
こういうExcelファイルがあったとします

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289210/eb4ac69e-c2f4-8798-4b4c-e0294fa09665.png" width=600>

とりあえず、入力されてるセルから適当な値を取り出してみます

```python
import openpyxl as ex

wb = ex.load_workbook("./data.xlsx")    # Excelファイル選択
ws = wb["Sheet1"]                       # シート名選択
read_value = ws["A2"].value             # セル座標選択、値を取得

print(read_value)
```

```
田村 奈央
```

`Sheet1`の`A2`セルに入力されている`田村 奈央`という値が出力されました


5行目、`value = ws["A2"].value`のところを`value = ws["A2"]`とすると
値ではなく`Cell`**オブジェクトの取得**になるので、print文で表示させたい時はフィールドである`value`を持ってきてください


次は書き込みをやってみます

```python
wb = ex.load_workbook("./data.xlsx")    # Excelファイル選択
read_value = wb["Sheet1"]["A2"].value   # シート、セル座標選択、値を取得

wb["Sheet2"]["B1"] = read_value         # 書き込み
wb.save("./data.xlsx")                  # 保存
```

上半分はさっきとほぼ同じです
まっさらなシート"Sheet2"を用意しておき、B1セルに書き込みます


最終行、`save`メソッドで上書き保存するのを忘れずに
(この部分の処理が無いとExcelファイルに反映されません)


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289210/88cd6892-2aaf-64ca-1f12-1ce2754d4fbc.png" width=400>

当然ですが、その部分にあった入力値は消えてしまいます。不安な方は別名で保存しておきましょう


## 行列番号でのセル座標指定
アドレスでのセル番地指定(先程までの座標指定方法)だと、loop処理などで扱いにくいケースが出てきます
そこで、行番号と列番号での座標指定を行ってみます

```python
wb = ex.load_workbook("./data.xlsx")
ws = wb["Sheet1"]

read_value_01 = ws["A2"].value      # セル番地指定
read_value_02 = ws.cell(2,1).value  # 行列番号指定

print(read_value_01, read_value_02)
```

```
田村 奈央 田村 奈央
```

`for`でインデックスを指定する時などはこっちの方が扱いやすい気がします
自分は行と列がいつもごっちゃになってしまうのでラッパーを作ったりしています

:::note warn
行と列共にインデックスは「1」からのスタートとなっているので注意
(「0」以下の値を指定するとERRORになる)
:::

## 注意点

### 関数を入力しているセルの読み込み
先程と同じシートにおいて、
「年齢」列のデータの読み込みを行ってみます


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289210/eb4ac69e-c2f4-8798-4b4c-e0294fa09665.png" width=600>


```python
wb = ex.load_workbook("./data.xlsx")
ws = wb["Sheet1"]

read_value = ws["E2"].value
print(read_value)
```

```
=DATEDIF(D2,$G$2,"Y")
```

表示されている`38`という値を取ってきてほしいのに、数式を取得してしまいました

このように、**関数によって値を算出しているセル**の場合
算出された値ではなく、入力されている**関数が読み込まれ**ます

もし値そのものを読み込みたい場合は
`load_workbook`メソッド内の引数`data_only`の値を`True`にしてExcelファイルをロードしておきましょう

```python
wb = ex.load_workbook("./data.xlsx", data_only=True) # 表示されている値として取得
ws = wb["Sheet1"]

read_value = ws["E2"].value
print(read_value)
```

```
38
```


### 結合セルの扱い
Excelにはセル結合という**忌まわしき機能**が存在します
シートの見栄えは良くなるのですが、**結合セルの値は左上部分の座標のみに記録されている**ため、データとして処理する際には困ります

具体例を見てみましょう

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289210/159c90d6-e747-56a5-c1e4-a7dec1685bd1.png" width=600>

上述のようなシートにおいて、データを人数分出力するコードを書いてみます

```python
wb = ex.load_workbook("./data.xlsx", data_only=True)
ws = wb["Sheet1"]

for row in range(2, 19+1):
    for colmun in range(1, 5+1):
        print(ws.cell(row, colmun).value, end=", ")
    print() # 改行
```

```
男, 安井 人志, やすい ひとし, 1960-01-30 00:00:00, 61,
None, 中山 寿明, なかやま としあき, 1999-12-04 00:00:00, 21,
None, 岩田 一徳, いわた いっとく, 1945-06-28 00:00:00, 75,
None, 重松 真一, しげまつ しんいち, 1960-12-13 00:00:00, 60,
None, 高山 雅彦, たかやま まさひこ, 1982-11-27 00:00:00, 38,
None, 相田 幸平, あいだ こうへい, 1967-04-09 00:00:00, 53,
None, 三原 丈史, みはら たけし, 1974-03-31 00:00:00, 46,
None, 中岡 将也, なかおか まさや, 1976-02-25 00:00:00, 44,
None, 徳永 明慶, とくなが あきよし, 1978-11-22 00:00:00, 42,
女, 及川 えみ, おいかわ えみ, 1980-05-31 00:00:00, 40,
None, 小野寺 淳子, おのでら あつこ, 1944-04-14 00:00:00, 76,
None, 布川 怜奈, ふかわ れいな, 1961-08-04 00:00:00, 59,
None, 瀬戸 かおり, せと かおり, 1950-03-12 00:00:00, 70,
None, 長崎 路子, ながさき みちこ, 1974-03-08 00:00:00, 46,
None, 滝 奈央, たき なお, 1954-02-09 00:00:00, 67,
None, 小野 あおい, おの あおい, 1974-04-14 00:00:00, 46,
None, 竹下 恵梨香, たけした えりか, 1988-05-28 00:00:00, 32,
None, 本村 紗季, ほんむら さき, 1955-05-30 00:00:00, 65,
```

**性別**の出力に注目してみてください

値がある人とない人がいることがわかります
これは結合セルの左上部分以外の座標には、内部的に値が存在しないことによる現象です


結合セルを含んだシートを処理する場合は十分に気をつけてください
一番良いのは、**結合セルを使わない**ことです


## リンクなど
* [openpyxl:公式ドキュメント](https://openpyxl.readthedocs.io/en/stable/api/openpyxl.html#)
* [なんちゃって個人情報](http://kazina.com/dummy/)
  * 本記事で使用したデータはこちらのサイトで自動生成しました