0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python で Word 文書の検索と置換を実現する:テキスト、正規表現、画像とテーブル

0
Posted at

検索と置換は Word 文書処理で最も頻繁に行われる操作である。契約書の社名変更、報告書の日付更新、テンプレートのプレースホルダーを実際の内容に差し替え——数十件の文書を手作業で処理するのは時間がかかり、ミスも生じやすい。プログラムで処理すれば、一括操作できるだけでなく、テキストを画像やテーブルなどの構造化コンテンツに置き換えることも可能になる。これは手作業の検索置換では実現できない機能である。

本記事では Free Spire.Doc for Python を使い、Word の検索置換の主要なパターンを示す:テキストの精密置換、正規表現マッチング置換、検索とハイライト、画像への置換、テーブルへの置換、そして複数文書の一括処理。

pip install spire.doc.free

以下の例ではサービス契約文書(sample.docx)を入力として使用する。文書には社名 TechCorp Solutions、クライアント名 Global Systems Inc、日付 January 15, 2026、三つのタグ #TAG001 #TAG002 #TAG003、および [LOGO] と [PRICING_TABLE] の二つのプレースホルダーが含まれている。

原始文書


テキスト置換:精密マッチングとオプション制御

Document.Replace メソッドは四つの引数を受け取る:検索テキスト、置換テキスト、大文字小文字を区別するか、完全一致か。完全一致を True にすると、"Provider" を検索しても "Providers" の部分文字列にはマッチしない。

from spire.doc import *
from spire.doc.common import *

document = Document()
document.LoadFromFile("sample.docx")

# 社名を置換
document.Replace("TechCorp Solutions", "Innovatech Partners", False, True)

# クライアント名を置換
document.Replace("Global Systems Inc", "Apex Dynamics Ltd", False, True)

# 日付を置換
document.Replace("January 15, 2026", "March 20, 2026", False, True)

document.SaveToFile("after_text_replace.docx", FileFormat.Docx)
document.Close()

実行後、文書内の三箇所のテキストが置換される:

Text replacement complete: after_text_replace.docx
Replaced: TechCorp Solutions -> Innovatech Partners
Replaced: Global Systems Inc -> Apex Dynamics Ltd
Replaced: January 15, 2026 -> March 20, 2026

Replace メソッドは文書本体、ヘッダー・フッター、テーブルセル内のすべての段落を走査する。手動で段落を逐次検索する必要はない。第三引数 False は大文字小文字を区別することを示し、第四引数 True は完全一致のみをマッチさせる。"cat" を "dog" に置換したいが "category" に影響させたくない場合、完全一致を使えば誤置換を防げる。

テキスト置換効果


正規表現マッチング置換

置換対象のテキストが固定の文字列ではなく固定のパターンを持つ場合、正規表現でマッチングする。文書中の #TAG001、#TAG002、#TAG003 は変更管理タグであり、一律で "RESOLVED" に置き換える必要がある。

Spire.Doc は Regex クラスと FindAllPattern メソッドを提供する。FindAllPattern はマッチしたすべてのテキスト選択オブジェクトを返し、それぞれの Text プロパティを変更することで置換が完了する:

from spire.doc import *
from spire.doc.common import *

document = Document()
document.LoadFromFile("sample.docx")

# 正規表現を作成(#TAG の後に英数字が続くパターン)
regex = Regex("#TAG\\w+", RegexOptions.none)

# すべてのマッチ項を検索
textSections = document.FindAllPattern(regex)

# 各項のテキストを置換
for ts in textSections:
    textRange = ts.GetAsOneRange()
    textRange.Text = "RESOLVED"

document.SaveToFile("after_regex_replace.docx", FileFormat.Docx)
document.Close()

実行結果:

Found 3 regex matches
Regex replacement complete: after_regex_replace.docx
Pattern: #TAG\w+ -> RESOLVED
Matched: #TAG001, #TAG002, #TAG003

Regex クラスは .NET の正規表現構文を使用する。\\w+ は一つ以上の英数字文字にマッチする。RegexOptions.none は特別なオプション(大文字小文字の無視、複数行モードなど)を使用しないことを示す。FindAllPattern が返す各 TextSelection オブジェクトは GetAsOneRange() で TextRange を取得し、Text プロパティを直接設定すればテキスト内容が置換され、元の書式は保持される。

正規表現置換は規則性のあるテキストの一括処理に適している:日付フォーマットの統一(\d{4}/\d{2}/\d{2} → \d{4}-\d{2}-\d{2})、メールアドレスのマスキング、番号プレフィックスの更新など。


テキスト検索とハイライトマーク

テキストを置換するのではなく、位置を特定してマークしたい場合がある。FindAllString メソッドは文書内のすべてのマッチ位置を返し、GetAsOneRange() でテキスト範囲を取得した後、ハイライトカラーを設定できる:

from spire.doc import *
from spire.doc.common import *

document = Document()
document.LoadFromFile("sample.docx")

# "Agreement" を検索して黄色でハイライト
selections = document.FindAllString("Agreement", False, True)
for selection in selections:
    selection.GetAsOneRange().CharacterFormat.HighlightColor = Color.get_Yellow()

# "Provider" を検索して緑色でハイライト
selections = document.FindAllString("Provider", False, True)
for selection in selections:
    selection.GetAsOneRange().CharacterFormat.HighlightColor = Color.get_Green()

# "Client" を検索してオレンジ色でハイライト
selections = document.FindAllString("Client", False, True)
for selection in selections:
    selection.GetAsOneRange().CharacterFormat.HighlightColor = Color.FromArgb(255, 255, 128, 0)

document.SaveToFile("after_highlight.docx", FileFormat.Docx)
document.Close()

実行後、各キーワードの出現回数が出力される:

Found 'Agreement': 5 occurrences, highlighted yellow
Found 'Provider': 3 occurrences, highlighted green
Found 'Client': 3 occurrences, highlighted orange
Highlight complete: after_highlight.docx

"Agreement" は 5 回出現する——タイトルに 1 回、導入段落に 2 回(引用符付きと引用符なし各 1 回)、終了条項に 1 回、秘密保持条項に 1 回。FindAllString の検索範囲は文書本体全体をカバーし、単一段落やセクションに限定されない。

Color.FromArgb の四つの引数は透明度(0–255)、赤、緑、青の成分である。Color.get_Yellow() と Color.get_Green() はプリセットカラーのショートカットメソッドで、Color.FromArgb(255, 255, 128, 0) は異なるキーワードを区別するためのオレンジ色をカスタム定義している。

検索ハイライト効果


テキストを画像に置換

プレースホルダーテキストを画像に置き換えることができる。文書中の [LOGO] プレースホルダーを会社のロゴ画像に置き換える必要がある。FindAllString でプレースホルダーの位置を見つけ、DocPicture オブジェクトを作成して画像を読み込み、テキスト範囲が属する段落に画像を挿入して元のテキストを削除する:

from spire.doc import *
from spire.doc.common import *

document = Document()
document.LoadFromFile("sample.docx")

# すべての [LOGO] プレースホルダーを検索
selections = document.FindAllString("[LOGO]", False, False)

for selection in selections:
    # 画像オブジェクトを作成してファイルを読み込み
    pic = DocPicture(document)
    pic.LoadImage("logo.png")

    # テキスト範囲の段落内での位置を取得
    textRange = selection.GetAsOneRange()
    index = textRange.OwnerParagraph.ChildObjects.IndexOf(textRange)

    # 画像を先に挿入してからテキストを削除
    textRange.OwnerParagraph.ChildObjects.Insert(index, pic)
    textRange.OwnerParagraph.ChildObjects.Remove(textRange)

document.SaveToFile("after_image_replace.docx", FileFormat.Docx)
document.Close()

実行結果:

Found '[LOGO]' placeholder: 1 occurrence(s)
Image replacement complete: after_image_replace.docx
Replaced [LOGO] with logo.png

挿入順序を逆にしてはいけない:必ず Insert を先に行い、その後に Remove する。テキスト範囲を先に削除すると、ChildObjects のインデックスが前へずれ、画像が誤った位置に挿入される。DocPicture のコンストラクタ引数は所属文書オブジェクトで、LoadImage は画像ファイルのパスを受け取る。PNG、JPEG、BMP などの一般的な形式をサポートしている。

画像置換効果


テキストをテーブルに置換

プレースホルダーをテーブルに置き換えるロジックは画像の場合とは異なる。テーブルはブロックレベルの要素であり、段落の子オブジェクトには挿入できない——置き換える対象はプレースホルダーを含む段落全体である。FindString(単数形)は最初のマッチ位置を返し、OwnerParagraph と OwnerTextBody で段落を特定し、段落の位置にテーブルを挿入して元の段落を削除する:

from spire.doc import *
from spire.doc.common import *

document = Document()
document.LoadFromFile("sample.docx")

section = document.Sections[0]

# [PRICING_TABLE] プレースホルダーを検索
selection = document.FindString("[PRICING_TABLE]", False, False)

# プレースホルダーが属する段落とドキュメント本体を取得
range = selection.GetAsOneRange()
paragraph = range.OwnerParagraph
body = paragraph.OwnerTextBody
index = body.ChildObjects.IndexOf(paragraph)

# 4 行 3 列の価格テーブルを作成
table = section.AddTable(True)
table.ResetCells(4, 3)

# ヘッダー行を埋める
header = ["Service Item", "Unit Price (USD)", "Quantity"]
for col, text in enumerate(header):
    cell = table.Rows[0].Cells[col]
    cellParagraph = cell.AddParagraph()
    cellParagraph.AppendText(text)

# データ行を埋める
data = [
    ["Cloud Infrastructure", "2,500/month", "1"],
    ["Data Migration", "8,000", "1"],
    ["Technical Support", "1,200/month", "1"],
]
for rowIdx, rowData in enumerate(data):
    for col, text in enumerate(rowData):
        cell = table.Rows[rowIdx + 1].Cells[col]
        cellParagraph = cell.AddParagraph()
        cellParagraph.AppendText(text)

# プレースホルダー段落をテーブルで置換
body.ChildObjects.Remove(paragraph)
body.ChildObjects.Insert(index, table)

document.SaveToFile("after_table_replace.docx", FileFormat.Docx)
document.Close()

実行結果:

Found '[PRICING_TABLE]' placeholder
Table replacement complete: after_table_replace.docx
Replaced [PRICING_TABLE] with 4x3 pricing table

section.AddTable(True) の引数 True はデフォルトのテーブル罫線スタイルを適用することを示す。ResetCells(4, 3) は 4 行 3 列の空のテーブルを初期化し、その後 Rows[i].Cells[j] で各セルにアクセスして段落テキストを追加する。body.ChildObjects はドキュメント本体の子オブジェクトコレクションで、Remove で段落を削除し、Insert で元の位置にテーブルを挿入して、文書構造の連続性を保つ。

テーブル置換効果


複数文書のバッチ処理

実際のシナリオでは、ディレクトリ内の複数の文書に対して同じ置換操作を実行することがよくある。Python のファイル走査機能と組み合わせて、置換ルールをリストとして定義し、各ファイルをループ処理する:

import os
import shutil
from spire.doc import *
from spire.doc.common import *

# 置換ルールを定義:(検索テキスト, 置換テキスト, 大文字小文字区別, 完全一致)
replacements = [
    ("TechCorp Solutions", "Innovatech Partners", False, True),
    ("Global Systems Inc", "Apex Dynamics Ltd", False, True),
    ("January 15, 2026", "March 20, 2026", False, True),
]

# バッチ入力ディレクトリを準備(3 件の文書コピー)
batch_dir = "batch_input"
os.makedirs(batch_dir, exist_ok=True)
for i in range(1, 4):
    shutil.copy("sample.docx", os.path.join(batch_dir, f"agreement_{i}.docx"))

# ディレクトリ内のすべての .docx ファイルを処理
output_dir = "batch_output"
os.makedirs(output_dir, exist_ok=True)

for filename in sorted(os.listdir(batch_dir)):
    if not filename.endswith(".docx"):
        continue

    doc = Document()
    doc.LoadFromFile(os.path.join(batch_dir, filename))

    for findText, replaceText, isCase, isWhole in replacements:
        doc.Replace(findText, replaceText, isCase, isWhole)

    doc.SaveToFile(os.path.join(output_dir, filename), FileFormat.Docx)
    doc.Close()
    print(f"  Processed: {filename}")

実行結果:

Created 3 documents in batch_input/
  Processed: agreement_1.docx
  Processed: agreement_2.docx
  Processed: agreement_3.docx
Batch processing complete: 3 documents processed
Output directory: batch_output/

置換ルールはループの外で定義し、すべての文書に統一して適用する。文書ごとに異なる置換ルールが必要な場合は、ファイル名でルール辞書をマッチングすればよい。Document オブジェクトは各ループで作成・クローズし、メモリの蓄積を避ける。


適用シーンと組み込み方

以上の六つの操作は Word の検索置換の主要なニーズをカバーする。テキスト置換は社名変更や日付更新などのピンポイント修正に適している。正規表現置換は規則性のあるテキストの一括処理に向く。検索ハイライトは文書レビューやキーワード定位に便利である。画像への置換とテーブルへの置換はテンプレート埋めに適している——テンプレートにプレースホルダーを配置し、実行時に実際の内容に置き換える。バッチ処理は単一文書の操作をディレクトリレベルに拡張し、契約書の一括生成や報告書の一括更新に対応する。

業務プロセスにおいて、検索置換は通常、文書処理パイプラインの一環として位置づけられる。上流システム(データベース、Excel、API)が置換データを提供し、Python スクリプトがデータを読み取り、テンプレートを読み込み、置換を実行し、文書を出力する。下流にはメール送信、アーカイブ保存、承認フローが接続される。置換ルールは JSON や YAML の設定ファイルで管理し、スクリプト内にハードコードしないようにできる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?