【Python】openpyxlでエクセルの「定義された名前」から、セルの値を取得

openpyxlで、エクセルファイルからデータを取得するとき、エクセルの「定義された名前」で設定された名前で取得した方が都合が良いことがあります。

import openpyxl 

wb = openpyxl.load_workbook('test.xlsx', read_only=True)
ws = wb['Sheet1']
ws["h3:i6"]

上の様な書き方をした場合、エクセルに行や列が追加されると、ソースコードの編集が必要になり、実に具合が良くありません。そのため、シートの保護を設定をしたくなりますが、エクセル入力者の自由を奪うことになります。

そこで、エクセルのセルの範囲に名前を付けて、その名前で値を取得するようにします。下の図では、Sheet1の”H3:I6″に”book_table_data”と名前を定義しています。

名前によるセルの値の取得方法は2種類

“book_table_data”の値の取り出し方は、下の通りです。

名前の範囲がブックの時の値の取得方法

import openpyxl

def read_table_by_book(xlsx_path: str, table_name: str) -> None:

    wb = openpyxl.load_workbook(xlsx_path, read_only=True)
    names = wb.defined_names[table_name].destinations	# (1)
    for sheet_name, range_name in names:
        ws = wb[sheet_name]
        rg = ws[range_name]
        print(sheet_name)
        for row in rg:
            line = [col.value for col in row]
            print(line)

read_table_by_book('test.xlsx', 'book_table_data')

Workbookのdefined_namesに、excelで付けた名前”book_table_data”を指定して、シート名とセル範囲を取得しています。その際、ジェネレータを利用しているため、複数の「シートとセル範囲」を取得できます。

ところが、(1)で取得できるシート名とセル範囲は、名前の定義の範囲に「ブック」を指定したときのみです。「シート」にした場合は、取得できません。取得しようとすると、(1)で例外が発生してしまうのです。

上図にある、table_dataは範囲が「シート」であるため、例外が発生します。

名前の範囲がブックかシートか確認する方法

エクセルファイルを開いて「数式タブを開いて・・・」と確認しても良いのですが、ファイルをテキストエディタで開いて見ることもできます。エクセルファイルの拡張子をzipにして展開して、workbook.xmlを探します。

workbook.xmlをテキストエディタで開きます。

localSheetIdが設定されていると、その名前で取得しようとしたとき、(1)で例外が発生します。

名前の範囲がシートの時の値の取得方法

そこで、名前の範囲が「シート」の時は別の方法を取ります。

import openpyxl

def read_table_by_sheet(xlsx_path: str, sheet_name: str, table_name: str) -> None:

    wb = openpyxl.load_workbook(xlsx_path, read_only=True)

    sheet_id: int = wb.sheetnames.index(sheet_name)
    attr_text: str = wb.defined_names.get(table_name, sheet_id).attr_text	# (2)
    print(attr_text)

    ws = wb[sheet_name]
    rg = ws[attr_text.split('!')[1]]
    print(sheet_name)
    for row in rg:
        line = [col.value for col in row]
        print(line)

read_table_by_sheet('test.xlsx', 'Sheet1', 'table_data')

(2)の様にWorkbookのdefined_namesで、シートIDと名前を指定すると、”Sheet1!$C$3:$F$7″とシート名とセル範囲が取得できます。後は、文字列の分割でセル範囲を分離して、値を取得できます。

コメント

タイトルとURLをコピーしました