python-docxでWord文書を作成する
実用的なケース
Wordをプログラムで操作する上で実用的なケースを考えました。やはり、テンプレートが指定されている文書をcsvなどからデータを取り込んで自動編集する、という局面で役に立つのではないでしょうか。今回はネットで拾ってきたサンプルの請求書ファイルを編集するというところまでやっていこうと思います。
テンプレートファイルの中身を解析する
from docx import Document import os print(f'{os.path.abspath(os.curdir)=}') document = Document('iv01.docx') print(f'{len(document.paragraphs)=}') for paragraph in document.paragraphs: print(f'{paragraph.text=}') print(f'{len(document.tables)=}') for table in document.tables: print(f'{len(table.rows)=}') for row in table.rows: print(f'{len(row.cells)=}') for cell in row.cells: paragraphs = cell.paragraphs for paragraph in paragraphs: print(f'table {paragraph.text=}')
以上のコードを実行すると、全てのバラグラフとテーブル値を出力できます。 paragraphsやtablesといったオブジェクトはコレクションなので、インデックスで要素にアクセス出来ます。 Wordのパラグラフやテーブル値がどのインデックスに位置するかを、上記コードで出力した文字列から解析します。
CSV
複数の請求先のデータを取り込みます。 請求先ごとに請求書を作る必要があります。
請求先,品名,数量,単価 会社A,商品A,1,1000 会社A,商品B,2,2000 会社A,商品C,3,3000 会社B,商品D,4,4000 会社B,商品E,5,5000 会社B,商品F,6,6000 会社B,商品G,7,7000
Word編集
請求先名称や日付、商品のテーブル行列がどのインデックスに位置するかを変数に保持します。 csvからデータを取り込んで、請求先名称の集合を取得します。それをメインのループで回して、請求先毎の請求書を作ります。 paragraphsやtablesに先程保持したインデックスを指定して、text値を編集します。
from docx import Document import pandas as pd from datetime import date from japanera import Japanera, EraDate jaera = Japanera() c_era = jaera.era(date.today()) template_name = 'iv01.docx' csv_path = './iv01.csv' pr_date = 0 pr_billing_address = 1 trc_billing_amount = (2, 3) trc_subtotal = (15, 7) trc_tax = (16, 7) trc_total = (17, 7) tax_rate = 0.08 items_row_start_idx = 6 c_item_name = 1 c_amount = 5 c_unit_price = 6 c_total = 7 df = pd.read_csv(csv_path) print(df) billing_address_set=set(df["請求先"]) print(f'{billing_address_set=}') for billing_address in billing_address_set: doc_name = f'請求書_{billing_address}.docx' document = Document(template_name) paragraphs = document.paragraphs table = document.tables[0] paragraphs[pr_date].text = c_era.strftime(date.today(), '%-E%-O年%m月%d日') paragraphs[pr_billing_address].text = f'{billing_address} 御中' billing_data = df[df['請求先'] == billing_address] print(billing_data) current_row_idx = items_row_start_idx subtotal = 0 for data in billing_data.values: print(data) table.rows[current_row_idx].cells[c_item_name].paragraphs[0].text = data[1] table.rows[current_row_idx].cells[c_amount].paragraphs[0].text = str(data[2]) table.rows[current_row_idx].cells[c_unit_price].paragraphs[0].text = str(data[3]) row_total = data[2] * data[3] table.rows[current_row_idx].cells[c_total].paragraphs[0].text = str(row_total) subtotal += row_total current_row_idx +=1 table.rows[trc_subtotal[0]].cells[trc_subtotal[1]].paragraphs[0].text = str(subtotal) tax = subtotal * tax_rate table.rows[trc_tax[0]].cells[trc_tax[1]].paragraphs[0].text = str(tax) table.rows[trc_total[0]].cells[trc_total[1]].paragraphs[0].text = str(subtotal + tax) table.rows[trc_billing_amount[0]].cells[trc_billing_amount[1]].paragraphs[0].text = str(subtotal + tax) document.save(doc_name)
結果