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?

openpyxl で保存し直すと消えるもの。xlsx のセルを 1 つだけ書き換える方法

0
Posted at

Excel とスプレッドシートを AI が操作するイメージ

人から受け取った xlsx のセルを 1 つ書き換えて返す、という作業を Python で自動化するとき、openpyxl で開いて保存し直すと、ファイルの中身が一部消えることがあります。Claude Code に Excel と Google スプレッドシートを同じ条件で操作させて比べる検証をしたときに見つけたので、確かめた結果と、消さずに書き換える方法をまとめます。比較の全体は元の記事に書きました。この記事では、保存し直しで何が消えるかと、xlsx の XML を直接書き換える方法に絞ります。

試したファイル

次の部品を入れた xlsx を作りました。

  • テキストボックス 2 つ
  • コメント 1 つ
  • プルダウン(入力規則)1 つ
  • フィルタの条件 1 つ
  • 非表示の行 10 行
  • カスタムプロパティ 1 つ

B2 のセル 1 つに名前を書き込み、4 通りの方法で保存しました。openpyxl は 3.1.5 です。

項目 元のファイル openpyxl で保存し直し XML を直接書き換え スプレッドシートに変換して戻す
ファイル内の部品数 15 13 15 13
テキストボックス 2 0 2 2
コメント 1 1 1 1
入力規則 1 1 1 1
フィルタの条件 1 1 1 0
非表示の行 10 10 10 20(全部)
カスタムプロパティ 1 1 1 0

openpyxl で保存し直すと、テキストボックスが 2 つとも消えました。「ここに社印を押してください」のような案内が、エラーも出さずに消えます。シートに置いた画像は、別に試したところ、保存し直しても残りました。

スプレッドシートに変換して編集し、Excel に戻す方法も試しました。テキストボックスは残りましたが、フィルタの条件が消え、データの行がすべて非表示の状態で保存されました。

取引先から受け取ったヒアリングシートでも同じことが起きました。openpyxl で開いて保存し直しただけで、ファイル内の部品が 66 から 20 に減り、シート名も変わりました。

xlsx は XML を zip にまとめたもの

xlsx の中身は、XML のファイルを zip でまとめたものです。書き換えたいセルの XML だけを差し替えて、ほかのファイルは 1 バイトも変えずにコピーすれば、部品は何も失われません。上の表の「XML を直接書き換え」の列は、この方法の結果です。

import re, zipfile
from xml.sax.saxutils import escape

def set_cell(src, dst, sheet_xml, ref, text):
    """シートの XML の中で、セル 1 つだけを文字列で書き換える。ほかのファイルはそのままコピーする"""
    row = re.sub(r"\D", "", ref)
    value = escape(text)  # & < > をエスケープしないと、ファイルが壊れる

    def new_cell(attrs):
        attrs = re.sub(r'\s+t="[^"]*"', "", attrs)  # 型だけ差し替え、書式(s="…")は残す
        return f'<c{attrs} t="inlineStr"><is><t xml:space="preserve">{value}</t></is></c>'

    with zipfile.ZipFile(src) as zin, zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zout:
        for item in zin.infolist():
            data = zin.read(item.filename)
            if item.filename == sheet_xml:
                s = data.decode()
                if f'<c r="{ref}"' in s:
                    s = re.sub(rf'<c( r="{ref}"[^>]*?)(?:/>|>.*?</c>)',
                               lambda m: new_cell(m.group(1)), s, count=1, flags=re.S)
                else:  # セルが無ければ行の末尾に足す(右隣の列にセルが無い場合だけ使える)
                    s = re.sub(rf'(<row r="{row}"[^>]*>.*?)(</row>)',
                               lambda m: m.group(1) + new_cell(f' r="{ref}"') + m.group(2), s, count=1, flags=re.S)
                data = s.encode()
            zout.writestr(item, data)

set_cell("申込書.xlsx", "申込書_記入済み.xlsx", "xl/worksheets/sheet1.xml", "B2", "山田太郎")

気をつける点

書き込む値は、必ずエスケープしてから XML に入れます。「田中&鈴木」のように & や < を含む値をそのまま入れると、openpyxl では開けないファイルになりました。LibreOffice ではエラーにならず、その値だけが消えていました。壊れたことに気づきにくいので、エスケープは省かないでください。

元のセルに付いている書式の指定(s="…")も残します。セルを丸ごと差し替えると、記入欄の太字や背景色、罫線が消えます。上の関数で、書式付きのセルや & を含む値を書き込んでも、値と書式がそのまま残ることを確かめました。

この関数は、文字列を 1 か所書き込む最小限の例です。数値や日付を入れる場合や、シート名から XML のファイル名を調べる場合は、処理を足す必要があります。

AI エージェントに任せるとき

Claude Code などに記入を任せるときは、指示に次の 2 点を入れておくと安全です。

  • openpyxl で開いて保存し直さない
  • XML のセルだけを差し替える

人から受け取ったファイルを、AI が黙って作り直してしまうことがあるためです。

行や数式のずれ、画像の挿入、回数制限など、Excel とスプレッドシートの比較の全体は、Claude Code に Excel とスプレッドシート、どちらを作らせるか|15の操作と5つの観点で実測比較に書いています。

元記事

Claude Code に Excel とスプレッドシート、どちらを作らせるか|15の操作と5つの観点で実測比較
https://amanity.co.jp/news/claude-code-excel-vs-sheets

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?