チュートリアル

簡単にライブラリーを使用するためのチュートリアルコレクション

チュートリアル».NET»Spire.Presentation for .NET»保護»C#:PowerPoint プレゼンテーションを保護または保護解除する方法
2026-10-30

Python で Word の条件付き差し込み印刷を作成・実行する方法

会員向けの案内状や請求書の送付では、同じ文面を全員に送るのではなく、会員ランクや利用金額に応じて一文だけ差し替えたい場面がよくあります。こうした「相手によって内容が変わる差し込み印刷」を 条件付き差し込み印刷 と呼びます。

本記事では、Spire.Doc for Python を使って、Word 文書に IF フィールドを組み込んだテンプレートを作成し、CSV の行ごとに条件を評価しながら一人分ずつ文書を出力する方法を紹介します。会員種別による文面の切り替えと、累計利用額による数値条件の判定を、実際に動作するコードと出力例で確認していきます。

クイックナビゲーション


1. 条件付き差し込み印刷とは

差し込み印刷は、宛名などの可変部分をデータから差し込む仕組みです。通常の差し込み印刷では、どの宛先にも同じ本文が入ります。これに対して条件付き差し込み印刷では、データの値に応じて表示する文章そのものを切り替えられます。

たとえば、次のような使い方ができます。

  • 会員ランクが「VIP」の顧客にだけ、特別クーポンの案内を入れる
  • 累計利用額が一定以上の顧客にだけ、優待の一文を追加する
  • 契約種別ごとに、更新案内の文面を変える

Word では、この条件分岐を IF フィールド で表現します。差し込み印刷のフィールドと IF フィールドを組み合わせると、1 つのテンプレートから相手ごとに異なる文面を生成できます。

種類 テンプレート 出力される文面
通常の差し込み印刷 すべての宛先で共通 全員が同じ本文
条件付き差し込み印刷 条件ごとに複数の文面を保持 データに応じて文面が変わる

Python 側で文字列を組み立てて分岐させる方法もありますが、テンプレート側に条件を置いておくと、文面の修正を Word ファイルだけで完結できます。月ごとに案内文を差し替える運用では、この分離が大きな利点になります。


2. 実行環境とサンプルデータ

ライブラリのインストール

Spire.Doc for Python は pip でインストールできます。

pip install spire.doc

スクリプト側では、必要なクラスをまとめて読み込みます。

from spire.doc import *

pip を使わずにインストールする場合は、Spire.Doc for Python のダウンロードページ からパッケージを入手できます。

差し込むデータ

今回は、会員 5 名分のデータを Customers.csv として用意します。列は次の 5 つです。

列名 内容 例
姓 姓 佐藤
名 名 美咲
会員種別 文面の分岐に使う区分 VIP / 一般
累計利用額 数値比較に使う金額 452000
累計利用額表示 画面表示用に整形した金額 452,000

「累計利用額」と「累計利用額表示」を分けているのは、条件判定には桁区切りのない数値を、本文には読みやすい桁区切り付きの文字列を使うためです。この使い分けについては条件式のバリエーションで詳しく説明します。

実際の Customers.csv は次のとおりです。

差し込み印刷に使用する Customers.csv の内容

累計利用額表示 のようにカンマを含む値は、CSV のフィールドとしてダブルクォートで囲みます。csv.reader で読み込むと、引用符は外れた状態で 1 つの値として取得できます。

文字コードは UTF-8(BOM 付き)で保存すると、Excel で開いたときにも日本語が文字化けしません。Python 側では encoding="utf-8-sig" を指定して読み込みます。


3. IF フィールドの構造

Word の IF フィールドは、次の形で記述します。

{ IF 条件 "条件が真のときの文字列" "条件が偽のときの文字列" }

Word の画面では波かっこはフィールドの目印であり、通常は結果だけが表示されます。フィールドコードを表示すると、条件式と 2 つの文面を確認できます。

IF フィールドは、内部的に次の 5 つの要素で構成されます。

要素 役割
フィールド開始 フィールドの始まりを示す
フィールドコード IF に続く条件式と 2 つの文面
区切り コードと結果の境界を示す
フィールド結果 条件を評価した結果として表示される文字列
フィールド終了 フィールドの終わりを示す

Spire.Doc for Python では、この構造を API で組み立てます。フィールド開始とコードの先頭は IfField オブジェクトが担い、条件式の残りは段落に追加したテキストと差し込みフィールドで構成します。最後に FieldEnd のマークを追加し、それを IfField.End に設定することで、1 つの IF フィールドとして閉じます。

比較に使える主な演算子は次のとおりです。

演算子 意味 例
= 等しい IF 会員種別 = "VIP"
<> 等しくない IF 会員種別 <> "一般"
> より大きい IF 累計利用額 > 100000
>= 以上 IF 累計利用額 >= 300000
< より小さい IF 累計利用額 < 100000
<= 以下 IF 累計利用額 <= 300000

文字列はダブルクォートで囲み、数値はそのまま記述します。両辺が数値として解釈できる場合は、文字列ではなく数値として比較されます。


4. テンプレートを作成する

まず、差し込み印刷の土台となる Word 文書を作成します。本文の途中に 2 つの IF フィールドを置き、1 つ目は会員種別、2 つ目は累計利用額で分岐させます。

from spire.doc import *


def add_para(section, text="", style="本文"):
    paragraph = section.AddParagraph()
    if text:
        paragraph.AppendText(text)
    paragraph.ApplyStyle(style)
    return paragraph


def build_template(path):
    doc = Document()
    section = doc.AddSection()
    section.PageSetup.PageSize = PageSize.A4()
    section.PageSetup.Margins.Top = 60
    section.PageSetup.Margins.Bottom = 60
    section.PageSetup.Margins.Left = 70
    section.PageSetup.Margins.Right = 70

    body_style = ParagraphStyle(doc)
    body_style.Name = "本文"
    body_style.CharacterFormat.FontName = "Yu Mincho"
    body_style.CharacterFormat.FontSize = 10.5
    body_style.ParagraphFormat.LineSpacing = 18
    body_style.ParagraphFormat.AfterSpacing = 4
    doc.Styles.Add(body_style)

    title_style = ParagraphStyle(doc)
    title_style.Name = "表題"
    title_style.CharacterFormat.FontName = "Yu Mincho"
    title_style.CharacterFormat.FontSize = 16
    title_style.CharacterFormat.Bold = True
    title_style.ParagraphFormat.HorizontalAlignment = HorizontalAlignment.Center
    title_style.ParagraphFormat.AfterSpacing = 18
    doc.Styles.Add(title_style)

    add_para(section, "会員の皆様へ ご利用感謝のご案内", "表題")
    add_para(section, "拝啓")
    add_para(section, " 平素より格別のご高配を賜り、厚く御礼申し上げます。")

    # 宛名(姓・名を差し込む)
    paragraph = section.AddParagraph()
    paragraph.AppendText(" ")
    paragraph.AppendField("姓", FieldType.FieldMergeField)
    paragraph.AppendText(" ")
    paragraph.AppendField("名", FieldType.FieldMergeField)
    paragraph.AppendText(" 様には、日頃より格別のご愛顧を賜り、心より感謝申し上げます。")
    paragraph.ApplyStyle("本文")

    add_para(section, " このたび、会員の皆様への感謝の気持ちを込めまして、下記のとおりご案内申し上げます。")
    add_para(section)

    # IF フィールド 1:会員種別による分岐
    paragraph = section.AddParagraph()
    if_field = IfField(doc)
    if_field.Type = FieldType.FieldIf
    if_field.Code = "IF "
    paragraph.Items.Add(if_field)

    # 条件式の中に差し込みフィールドを置く
    paragraph.AppendField("会員種別", FieldType.FieldMergeField)
    paragraph.AppendText(" = ")
    paragraph.AppendText('"VIP"')
    paragraph.AppendText(' " いつも特別なご支援をいただき、誠にありがとうございます。'
                         'VIP会員様限定の特別クーポンをお送りいたします。"')
    paragraph.AppendText(' " これからもご満足いただけるサービスをお届けしてまいります。"')

    # フィールド終了マークで IF フィールドを閉じる
    end_mark = paragraph.AppendFieldMark(FieldMarkType.FieldEnd)
    if_field.End = end_mark
    paragraph.ApplyStyle("本文")

    paragraph = section.AddParagraph()
    paragraph.AppendText(" 現在のご利用累計額は ")
    paragraph.AppendField("累計利用額表示", FieldType.FieldMergeField)
    paragraph.AppendText(" 円です。")
    paragraph.ApplyStyle("本文")

    # IF フィールド 2:累計利用額による数値判定
    paragraph = section.AddParagraph()
    amount_field = IfField(doc)
    amount_field.Type = FieldType.FieldIf
    amount_field.Code = "IF "
    paragraph.Items.Add(amount_field)

    paragraph.AppendField("累計利用額", FieldType.FieldMergeField)
    paragraph.AppendText(" >= ")
    paragraph.AppendText("300000")
    paragraph.AppendText(' " 300,000円以上のご利用をいただいたお客様には、'
                         '次回使える1,000円クーポンをご用意しました。"')
    paragraph.AppendText(' " 引き続き、お得な情報をお届けしてまいります。"')

    end_mark = paragraph.AppendFieldMark(FieldMarkType.FieldEnd)
    amount_field.End = end_mark
    paragraph.ApplyStyle("本文")

    add_para(section)
    add_para(section, " 今後とも変わらぬご愛顧のほど、よろしくお願い申し上げます。")
    add_para(section, "敬具")
    add_para(section)
    add_para(section, "株式会社サンプル商事")
    add_para(section, "営業部 山田 太郎")

    doc.SaveToFile(path, FileFormat.Docx)
    doc.Close()


build_template("ConditionalMailMerge.docx")

このコードで作成したテンプレートのフィールドコードを表示したものが次の画像です。宛名の差し込みフィールドと、2 つの IF フィールドの条件式が確認できます。

条件付き差し込み印刷のテンプレートと IF フィールドのコード

フィールドを組み立てるときのポイント

  • IfField.Code には "IF " までを設定し、条件式の残りは段落に追加していきます。Code に全文を詰め込むのではなく、差し込みフィールドと文字列を順番に並べるのが Spire.Doc の基本パターンです。
  • 条件式の中の差し込みフィールドは AppendField() で追加します。IF の直後に置くと、差し込み印刷の実行時に実際の値へ置き換わります。
  • 文面はダブルクォートで囲みます。日本語の文章をそのまま入れられるため、テンプレート側で文章を完結できます。
  • AppendFieldMark(FieldMarkType.FieldEnd) の戻り値を IfField.End に渡します。この対応付けによって、どこまでが 1 つの IF フィールドなのかが確定します。

5. CSV の全行を差し込んで出力する

テンプレートができたら、CSV の行ごとに差し込み印刷を実行し、1 人分ずつ Word ファイルとして保存します。

import csv
import os
from spire.doc import *

os.makedirs("Recipients", exist_ok=True)

with open("Customers.csv", encoding="utf-8-sig") as f:
    rows = list(csv.reader(f))

header = rows[0]

for row in rows[1:]:
    doc = Document("ConditionalMailMerge.docx")

    # 文書内の差し込みフィールド名を取得する
    field_names = doc.MailMerge.GetMergeFieldNames()

    # フィールド名をキーにして、CSV の対応する列から値を取り出す
    values = [row[header.index(name)] for name in field_names]

    # 差し込み印刷を実行する
    doc.MailMerge.Execute(field_names, values)

    # IF フィールドの条件を評価し、結果を文書に反映する
    doc.IsUpdateFields = True

    doc.SaveToFile(f"Recipients/{row[0]} {row[1]}.docx", FileFormat.Docx)
    doc.Close()

    print(f"{row[0]} {row[1]} 様の文書を出力しました。")

print(f"合計 {len(rows) - 1} 件の文書を出力しました。")

このスクリプトを mail_merge.py として保存して実行すると、出力した顧客ごとにログが表示されます。

条件付き差し込み印刷を実行したときのコンソール出力

GetMergeFieldNames() と値の対応

GetMergeFieldNames() は、文書内に現れる差し込みフィールドの名前を出現順で返します。返されるリストはテンプレートの並び順であり、CSV の列順とは一致しないことがあります。そのため、上のコードではフィールド名を手がかりに header.index(name) で列を引き当てています。

同じ名前のフィールドが文書内に複数ある場合は、リストに同じ名前が複数回含まれます。名前から値を引くこの方法なら、重複があっても正しい値が入ります。

MailMerge.Execute() に渡すフィールド名と値のリストは、同じ長さと同じ順序である必要があります。field_names をそのまま使って values を組み立てると、この対応が崩れません。

IsUpdateFields の役割

doc.IsUpdateFields = True は、保存時にフィールドを更新するための設定です。これを有効にすると、差し込み後の値を使って IF フィールドの条件が評価され、選ばれた側の文面がフィールド結果として文書に書き込まれます。条件分岐を含む差し込み印刷では、この 1 行が結果を確定させる重要なステップです。


6. 出力結果を確認する

5 名分の出力は、次のように条件ごとに異なる文面になります。

姓 名 会員種別 累計利用額(表示) 会員種別の分岐 金額の分岐
佐藤 美咲 VIP 452,000 VIP 向け 300,000 円以上
鈴木 健一 一般 128,000 通常 ポイント案内
高橋 由紀 VIP 86,000 VIP 向け ポイント案内
田中 誠 一般 315,000 通常 300,000 円以上
伊藤 さくら 一般 54,000 通常 ポイント案内

VIP かつ累計利用額が 300,000 円以上の佐藤 美咲様には、2 つの条件がともに成立した文面が出力されます。

VIP 会員向けに出力された条件付き差し込み印刷の文書

一方、一般会員で累計利用額が 300,000 円未満の伊藤 さくら様には、通常の文面とポイント案内が出力されます。

一般会員向けに出力された条件付き差し込み印刷の文書

同じテンプレートから、宛名・会員種別の文面・金額の案内がそれぞれ異なる文書が生成されていることを確認できます。出力ファイル名も 姓 名.docx の形式で保存されるため、送付先ごとの管理がしやすくなります。


7. 条件式のバリエーション

数値条件と表示用の値を分ける

金額のように桁区切りで表示したい値は、判定用のフィールドと表示用のフィールドを分けると扱いやすくなります。今回のテンプレートでは、次の 2 つを使い分けています。

  • 累計利用額(452000)— >= 300000 のような数値比較に使用
  • 累計利用額表示(452,000)— 本文への表示に使用

数値比較は桁区切りのない値で行い、読みやすさは表示用の文字列で確保する、という役割分担です。CSV の列を 1 つ増やすだけで、条件式を複雑にせずに両立できます。

3 つ以上の文面に分岐する

条件が 3 つ以上ある場合は、IF フィールドの偽の側にもう 1 つ IF フィールドを入れ子にします。次は、VIP・累計利用額 300,000 円以上・それ以外の 3 パターンに分ける例です。

paragraph = section.AddParagraph()

outer = IfField(doc)
outer.Type = FieldType.FieldIf
outer.Code = "IF "
paragraph.Items.Add(outer)
paragraph.AppendField("会員種別", FieldType.FieldMergeField)
paragraph.AppendText(' = "VIP" ')
paragraph.AppendText('"VIP会員様向けのご案内です。" ')
paragraph.AppendText('"')

inner = IfField(doc)
inner.Type = FieldType.FieldIf
inner.Code = "IF "
paragraph.Items.Add(inner)
paragraph.AppendField("累計利用額", FieldType.FieldMergeField)
paragraph.AppendText(' >= 300000 "上位会員様向けのご案内です。" "通常のご案内です。"')
inner_end = paragraph.AppendFieldMark(FieldMarkType.FieldEnd)
inner.End = inner_end
paragraph.AppendText('"')

outer_end = paragraph.AppendFieldMark(FieldMarkType.FieldEnd)
outer.End = outer_end
paragraph.ApplyStyle("本文")

外側の IF の偽の文面として、内側の IF フィールド全体を置く形です。分岐が増える場合は、この入れ子を必要な段数だけ繰り返します。分岐が多い場合は、入れ子の階層を整理しながら、Python 側で文面を切り替える方法と組み合わせると管理しやすくなります。

条件に合う顧客だけ別ファイルに集める

差し込みと同時に、条件に応じた出力先を切り替えることもできます。たとえば VIP 会員だけ別のフォルダに保存したい場合は、ループの中で row の値を確認して保存先を変えます。

folder = "Recipients/VIP" if row[2] == "VIP" else "Recipients/General"
os.makedirs(folder, exist_ok=True)
doc.SaveToFile(f"{folder}/{row[0]} {row[1]}.docx", FileFormat.Docx)

テンプレート側の条件分岐と、Python 側の出力先の振り分けを組み合わせると、送付作業の自動化がさらに進みます。


まとめ

本記事では、Spire.Doc for Python を使って Word の条件付き差し込み印刷を作成し、実行する手順を紹介しました。

  1. IF フィールドで文面を分岐する — IfField に "IF " のコードを設定し、条件式の中に差し込みフィールドを置き、FieldEnd で閉じます。
  2. CSV の行ごとに差し込み印刷を実行する — GetMergeFieldNames() でフィールド名を取得し、名前をキーに値を対応させて MailMerge.Execute() に渡します。
  3. IsUpdateFields で条件を確定する — 差し込み後に doc.IsUpdateFields = True を設定すると、IF フィールドの結果が文書に反映されます。

会員種別のような文字列条件と、累計利用額のような数値条件は、同じ IF フィールドの仕組みで扱えます。判定用と表示用の値を分けておけば、桁区切り付きの金額表示と数値比較を無理なく両立できます。

Word 文書の作成や PDF への変換と組み合わせる場合は、Python を使用して構造化された Word 文書を作成する方法や Python:Word を PDF に変換する方法もあわせてご覧ください。基本的な差し込み印刷の流れは、Python:Word 文書で差し込み印刷を作成・実行する方法で解説しています。

Spire.Doc for Python の全機能を評価したい場合は、30 日間の無料ライセンスを申請できます。


9. FAQ

Microsoft Word をインストールせずに条件付き差し込み印刷を実行できますか?

はい。Spire.Doc for Python は Word のアプリケーションに依存せず、Python スクリプトだけでテンプレートの作成と差し込み印刷を実行できます。Word をインストールしていない環境でも、同じコードで文書を生成できます。

IF フィールドを使わずに、Python 側で文面を分岐できますか?

はい。CSV の値を読み取って Python の if 文で文面を選び、その文字列を差し込む方法もあります。テンプレート側に IF フィールドを置く方法は、文面の修正を Word ファイルだけで完結させたい場合や、条件の追加を非エンジニアに任せたい場合に適しています。

金額をカンマ区切りで表示しながら、数値として比較するにはどうすればよいですか?

判定用と表示用のフィールドを分けます。数値比較には 452000 のような桁区切りのない値を使い、本文への表示には 452,000 のような整形済みの文字列を使います。本記事のサンプルでは 累計利用額 と 累計利用額表示 の 2 列でこれを実現しています。

3 つ以上の条件に分岐するにはどうすればよいですか?

IF フィールドの偽の側に、もう 1 つ IF フィールドを入れ子にします。本記事の条件式のバリエーションで、VIP・累計利用額 300,000 円以上・それ以外の 3 パターンに分けるコードを紹介しています。

差し込み後の文書を PDF で保存できますか?

はい。差し込み印刷を実行した Document オブジェクトに対して、doc.SaveToFile("output.pdf", FileFormat.PDF) を呼び出すと PDF として保存できます。メール添付用の配布や、レイアウトを固定した送付に利用できます。

出力ファイル名を顧客ごとに変えるにはどうすればよいですか?

SaveToFile() のパスに f 文字列を使い、CSV の値や連番を埋め込みます。本記事のサンプルでは f"Recipients/{row[0]} {row[1]}.docx" として、姓と名からファイル名を組み立てています。同じ名前の顧客がいる場合は、顧客 ID を加えると重複を避けられます。

Read 8 times