OpenPyXLを使ったExcel操作まとめ

OpenPyXLを利用したExcelをごにょごにょするツールを作成する機会があったので、実用ベースでの色々な操作方法（基礎的なものから幅広く）、注意点等をまとめておきます。

公式ドキュメント (http://openpyxl.readthedocs.io/en/default/)
# 環境
Windows 10
Python 3.8.1
OpenPyXL 3.0.3
Excel 2019
# 基本操作
### ブック操作
```python
import openpyxl

# 新規作成
new_wb = openpyxl.Workbook()

# 既存Excelファイル読込
filepath = r'C:\hoge\hogehoge.xlsx'
wb = openpyxl.load_workbook(filename=filepath, read_only=False)

# 保存
wb.save(filepath)

# 閉じる
wb.close()
```
#### *openpyxl.load_workbook()*
|引数|概要|
|----|----|
|read_only|読み取り専用（*True*）か書き込み可能（*False*）かを指定する。|
|keep_vba|VBAを読み込む（*True*）か読み込まない（*False*）かを指定する。<br>**※Falseで保存するとVBAが動かなくなるので注意！**|
|data_only|値のみ読み込む（*True*）か関数も読み込む（*False*）かを指定する。<br>**※*True*で保存すると関数が消えます（値で張り付けを行った時と同様の状態）。**<br>**※*False*の場合にCell.valueで関数を設定しているセルの値を取得すると数式が文字列で取得されてしまいます。**|
読み取り専用の場合等は`keep_vba=False`にしておくと多少読込が早くなると思います。
### シート操作
```python
# シートの取得
ws = wb.worksheets[0] #インデックス指定で取得（0が1シート目）
ws = wb["シート名"] #シート名指定で取得

# シートのインデックス取得
index = wb.index(ws)

# シート名の操作
ws_name = ws.title #シート名取得
ws.title = "シート名" #シート名変更
ws_name_list = wb.sheetnames #全シート名をリストで取得
ws_length = len(wb.sheetnames) #通常のリスト同様len()でシート数を取得可能

# シートの追加
ws_new = wb.create_sheet(title="新シート名", index=0) #1シート目に追加（index未指定の場合は末尾） 

# シートのコピー
ws_copy = wb.copy_worksheet(ws)

# シートの削除
wb.remove(ws)
wb.remove(wb.worksheets[-1]) #末尾のシートを削除
wb.remove_sheet(ws) #非推奨のようですがこっちでしか削除できない？ことがあったので一応

# タブの色を設定
ws.sheet_properties.tabColor = 'カラーコード'
```
### セル操作
```python
# 数値座標でセルの値を取得
cell_data = ws.cell(1, 2).value #B1の値を取得
row_num = cell_data.row #横軸の座標:1
column_num = cell_data.col_idx #縦軸の座標:2

# Excel座標でセル値を取得
cell_data = ws['C1'].value #C1の値を取得
cell_address = cell_data.coordinate #Excel座標:B1

# セルに値を設定
ws.cell(1, 2).value = cell_data #C1の値をB1に設定
```
基本的に数値座標の方が汎用性が高く、性能も良いです。
## その他の実用的操作
###範囲指定と各種ループ処理

```python
# Excel座標で範囲指定
ws_range = sheet['A1':'C20'] #A1:C20のセル範囲を取得
for row in ws_range: #上から下に行でループ
    for cell in row: #左上から右下にセルでループ
        print(cell.value) #範囲のセル値を順に出力

# 数値座標で範囲指定
for row in ws.iter_rows(min_row=1, min_col=1, max_row=20, max_col=3): #A1:C20のセル範囲で上から下に行でループ
    for cell in row: #左上から右下にセルでループ
        print(cell.value) #A1:C20のセルの値を順に出力

# シートループ処理
for ws in wb: #1シート目から順にループ
    # 行ループ処理
    for row in ws.iter_rows(min_row=1, max_row=20): #1～20行目まで行でループ
        if ws.cell(row[0].row, 3).value is None: # row[0]で処理対象の行情報を取得できる
            continue #空白セルの場合スキップ
        print(ws.cell(row[0].row, 3).value) #C1〜C20（空白セルを除く）の値を順に出力

# 列ループ処理
for col in ws.iter_cols(min_col=1, min_col=3): #1～3列目まで列でループ
    print(ws.cell(2, col[0].column).value) #A2,B2,C2の値を順に出力
```
こちらも数値座標の方が汎用性が高く、性能も良いです。
横軸座標の最大値/最小値、縦軸座標の最大値/最小値を任意の値、任意の組み合わせで指定可能。
とりあえずセル毎でループさせているドキュメントも多いですが、基本的に行または列毎でループし、数値座標でcellにアクセスする方が性能が良いです。
### 行・列操作
```python
# 行追加
ws.insert_rows(5, 2) #5行目に2行追
# 列追加
ws.insert_cols(5, 2) #5列目に2列追加

# 行削除
ws.delete_rows(6, 3) #6行目を3行削除
# 列削除
ws.delete_cols(6, 3) #6列目を3列削除
```
### 入力規則の設定
```python
from openpyxl.worksheet.datavalidation import DataValidation

# プルダウンを設定
dv = DataValidation(type="list", formula1="シート名!$A$1:$A$5")
    #formula1に選択値の範囲を指定（Excelと同様の記載でOK）
dv.add(ws.cell(2, 2)) #B2にプルダウンを設定
ws.add_data_validation(dv) #シートに入力規則を登録
```
`formula1='"A,B,C"'`のように値で指定することも可能。
ただ、以下のような注意点があります。（回避方法があればご教授下さいm(_ _)m）
**※ 重複したセル範囲のDataValidationを複数定義したワークブックをロードするとDataValidationが消える。**
**※ DataValidationを定義したシートをコピーしてもDataValidationはコピーされません。（コピーしたWorksheetオブジェクトに再度add\_data\_validation()することで定義されます）**
***
一旦終わります。ご覧頂きありがとうございました。
誤りや非効率な書き方等があるかもしれませんがご容赦ください。
適宜追加していくと思います。