Pythonでエクセルの差分検出ツールを作ってみよう

2025/02/24に公開

PythonでA列とB列を比較して差分部分を赤字にするツールを作成する方法をまとめようと思います
ツールはCopilotを使用して作成しております

参考サイトではpywin32というパッケージを使用しており、MacOSでは使用できなかったため、
MacOSでも同じようなことができるように作成してみました
こちらのコードはMacOS、windowsともに動作します

参考サイト

https://qiita.com/dbc_spr/items/92a16313fed44e703565

サンプルファイルの内容

ツール使用前

ツール使用後

開発環境

M1 Mac
MacOS v14.5
Python v3.9.6

使用するパッケージのインストール

pip3 install openpyxl

作成するコード

from openpyxl import load_workbook
from openpyxl.styles import Font
from openpyxl.cell.text import InlineFont
from openpyxl.cell.rich_text import TextBlock, CellRichText
from tkinter import Tk, filedialog
import difflib
import os

root = Tk()
root.withdraw()  # Tkinterのメインウィンドウを非表示にする

# 差分の書式指定
formatting = {"color": "FF0000", "b": True, "sz": 14} # ①
red_large_font = InlineFont(**formatting)

# 比較の最初の行を設定
comparison_start_row = 2 # ②

# エクセルファイルを選択
file = filedialog.askopenfilename(title='エクセルファイルを選択してください', filetypes=[("EXCEL", "*.xlsx")]) # ③

# エクセルファイルを開く
wb = load_workbook(file)
ws = wb.active

for row in range(comparison_start_row, ws.max_row + 1):
    # 比較対象のセルを取得
    before_cell = ws[f'A{row}'] # ④
    after_cell = ws[f'B{row}']
    word1 = ""
    word2 = ""
    
    if before_cell.value is None and after_cell.value is None:
        # セルの値が空の場合は何もしない
        pass
    elif before_cell.value is None:
        # セルの値が空の場合は、もう片方のセルを赤字にする
        after_cell.font = Font(**formatting)
    elif after_cell.value is None:
        # セルの値が空の場合は、もう片方のセルを赤字にする
        before_cell.font = Font(**formatting)
    elif before_cell.value != after_cell.value:
        # セルの値が異なる場合は、差分を取得する
        ndiff = difflib.ndiff(str(before_cell.value), str(after_cell.value)) # ⑤

        # 差分の箇所に$を付けて、視覚化する
        for i in ndiff:
            if i.startswith(" "):
                i = i[2:].replace(" ", "\u00A0")  # 半角の空白をノーブレークスペースに置き換え
                word1 += i
                word2 += i
            elif i.startswith("- "):
                i = i[2:].replace(" ", "\u00A0")  # 半角の空白をノーブレークスペースに置き換え
                word1 += f"${i}"
            elif i.startswith("+ "):
                i = i[2:].replace(" ", "\u00A0")  # 半角の空白をノーブレークスペースに置き換え
                word2 += f"${i}"

        rich_text_before = []
        i = 0

        while i < len(word1):
            if word1[i] == "$": # ⑥
                # $がある場合は、差分の書式を適用する
                i += 1
                rich_text_before.append(TextBlock(red_large_font, word1[i]))
            else:
                rich_text_before.append(word1[i])
            i += 1
        before_cell.value = CellRichText(*rich_text_before)

        rich_text_after = []
        i = 0
        while i < len(word2):
            if word2[i] == "$":
                # $がある場合は、差分の書式を適用する
                i += 1
                rich_text_after.append(TextBlock(red_large_font, word2[i]))
            else:
                rich_text_after.append(word2[i])
            i += 1
        after_cell.value = CellRichText(*rich_text_after)

wb.save(file)
os.system(f'open "{file}"')

コードの解説

①: 差分が発生した場合の書式を指定しています
  例のコードでは文字の色が#FF0000で太字, フォントサイズが14を指定しています
②: 比較するはじめの行を指定します
  このコードでは1行目にタイトルがあり、2行目から比較対象があるので2を指定しています
③: 比較対象のファイルはエクセルファイルのみなので、エクセルファイルのみ指定できるように
  しています
④: 比較前と後のセルの指定をしています
  このコードではA列は「変更前」、B列は「変更後」としています
⑤: ここで差分を検出しています
  試しにfor i in ndiff:の中にprint(i)を設定すると下記のように出力され、
 オとフが差分として検出されていることがわかります

+ オ
+ フ
  ホ
  ワ
  イ
  ト

⑥: word1とword2の差分部分の前に「$」が設定されているので、
  その文字に対して①で設定した書式を適用します
  なお、word2にはこのようなデータが入っています

$オ$フホワイト

Discussion