RAGで厄介なExcel文書を意味構造JSON化するOSSライブラリを作りました
はじめに
こんちは!製造業で社内SE的なのやってる人です。
最近RAG分野にハマっていますが、Excel文書が厄介者すぎるということに最近気が付きました。ZennでもRAGにおいて、このExcel文書をいかに扱うかの記事は定期的に見かけます。
先人のエンジニアの方々が様々な有効打を提示してくれていますが、私も別のアプローチでこの課題に取り組んでみたので共有します🔥
TL;DR
-
ExcelをCOM +
xlwings/openpyxlで構造化する自作ライブラリ「exstruct」。セル値だけでなく図形・グラフ・ハイパーリンク・印刷範囲・罫線由来の表までJSON/YAML/TOON形式で出力。 -
画像認識なしでフローチャート矢印方向やグラフ軸・系列も抽出でき、RAGで失われがちな情報を補完してLLM復元精度が向上。
-
デモで表+折れ線グラフ+フローチャートをJSON化→Markdown/mermaidへ高精度再構成。
-
制約: 高度抽出はExcel COM依存でWindows+Excel必須(他OSは簡易モードのみ)。
-
Codex/ChatGPTで開発し、
mkdocs-material+ GitHub PagesでAPIリファレンスを自動デプロイ。
想定している読者
- LLM/RAGに Excel文書を理解させるのに困っている
- OCRやVLMは重く、まずはルールベースで精度を上げたい
- 社内利用前提で Windows + Excel 環境が当たり前
作成したライブラリの紹介「ExStruct」
今回、プライベート個人でExcel文書をJSONなどの構造化データに変換するPythonライブラリを作ってみました。
ライブラリ名はExcel + Extract (抽出) + Structure (構造) で「ExStruct」です。
ざっくり言うとxlwingsなどのExcel COM参照ライブラリやopenpyxlを使って徹底的に構造化するだけのライブラリです。
何ができる
-
ブック内のセル値、図形、グラフ、ハイパーリンク、印刷範囲などを解析してLLM向けの構造化データに変換します。
-
構造化したデータをJSON/YAML/TOON形式などで出力します
-
シート内の罫線から表の範囲を推測します。
-
シートごとや印刷範囲ごとにデータ出力もできます
-
PDF/PNG出力もでき、LLMへの入力に役に立ちます
-
完全ルールベース解析なのでローカルPCで簡単に実行できます。
使い方
導入は以下のコマンドで可能です。
uv add exstruct
pip install exstruct
使用者がやることはExcel読み込み→構造化出力だけなのでシンプルなAPIを用意しています。
from pathlib import Path
from exstruct as xs
# 出力情報量を選択可能: "light", "standard", "verbose"
wb = xs.extract("input.xlsx", mode="standard")
xs.export(wb, Path("out.json"), pretty=False) # compact JSON
# pandasやopenpyxlチックなモデル操作・保存も可能
first_sheet = wb["Sheet1"]
for name, sheet in wb:
print(name, len(sheet.rows))
wb.save("out.json", pretty=True) # WorkbookData -> file
first_sheet.save("sheet.json") # SheetData → file
print(first_sheet.to_yaml()) # YAML出力
使用デモ①
RAGなどにおいてExcel文書内のグラフやテキストオブジェクトは大変厄介です。これらはpandasやopenpyxl、excel2jsonなどの既存の構造化ライブラリでは取得できず、工夫して取得する必要があります。
以下のような表データ、折れ線グラフ、図形で作られたフローチャートを用意しました。これを自作したエンジンで構造化してみます。

Excel文書によくいる奴らを集めてみた
ExStructはCLIでも実行可能です。以下のようなコマンドで出力します。
exstruct sample.xlsx --pretty > sample.json
出力される結果が以下です。(長いので一部省略)
{
"book_name": "sample.xlsx",
"sheets": {
"Sheet1": {
"rows": [
{
"r": 3,
"c": {
"1": "月",
"2": "製品A",
"3": "製品B",
"4": "製品C"
}
},
...
],
"shapes": [
{
"text": "開始",
"l": 148,
"t": 220,
"type": "AutoShape-FlowchartProcess"
},
{
"text": "入力データ読み込み",
"l": 132,
"t": 282,
"type": "AutoShape-FlowchartProcess"
},
{
"l": 193,
"t": 246,
"type": "AutoShape-Mixed",
"begin_arrow_style": 1,
"end_arrow_style": 2,
"direction": "N"
},
...
],
"charts": [
{
"name": "Chart 1",
"chart_type": "Line",
"title": "売上データ",
"y_axis_range": [
0.0,
200.0
],
"series": [
{
"name": "製品A",
"name_range": "Sheet1!$C$3",
"x_range": "Sheet1!$B$4:$B$9",
"y_range": "Sheet1!$C$4:$C$9"
},
...
],
"l": 377,
"t": 25
}
],
"table_candidates": [
"B3:E9"
]
}
}
}
いかがでしょうか?
セル値の他、テキストオブジェクトのテキストと座標、フローチャートの矢印の角度や矢のタイプ、グラフの範囲、表として検出した範囲などが網羅的にJSON化されています。
LLMに出力したJSONの内容を推論させてみる
上記はExcelブックを独自のjson形式に構造化したものです。 このブックに書かれている内容をmarkdownに変換してください。 mermaid形式での記述もOKです。
Copilotの回答はこちら
以下は、提供されたExcelブックの内容をMarkdown形式で再構成したものです。表、グラフ、フローチャートの3要素をそれぞれ適切に表現しています。
---
## 📊 売上データ表
| 月 | 製品A | 製品B | 製品C |
|------------|-------|-------|-------|
| 2025-01-01 | 120 | 80 | 60 |
| 2025-02-01 | 135 | 90 | 64 |
| 2025-03-01 | 150 | 100 | 70 |
| 2025-04-01 | 170 | 110 | 72 |
| 2025-05-01 | 160 | 120 | 75 |
| 2025-06-01 | 180 | 130 | 80 |
---
## 📈 売上データ(折れ線グラフ)
- グラフタイトル: **売上データ**
- グラフ種類: 折れ線グラフ(Line)
- Y軸範囲: 0 ~ 200
- データ系列:
- 製品A: 120 → 135 → 150 → 170 → 160 → 180
- 製品B: 80 → 90 → 100 → 110 → 120 → 130
- 製品C: 60 → 64 → 70 → 72 → 75 → 80
---
## 🔄 処理フロー(Mermaid フローチャート)
```mermaid
flowchart TD
A[開始]
B[入力データ読み込み]
C{形式は正しい?}
D[エラー表示]
E[1件処理]
F{残件あり?}
G[出力を生成]
H{メール送信?}
I[メール送信]
J[終了]
A --> B
B --> C
C -- いいえ --> D
C -- はい --> E
E --> F
F -- はい --> E
F -- いいえ --> G
G --> H
H -- はい --> I
H -- いいえ --> J
I --> J
```
各種データが詳細に高精度に推論されていることが分かります。
とくにフローチャートを画像認識無しでここまで理解できたのは驚きでした。
使用デモ②
もう一つ、製造業などでよくあるExcel手順書を解析してみます。

非常によく見かける系の手順書。セル結合多め
JSON化したものがこちら(今回はprettyオプションOFF)
{"book_name": "サンプル手順書.xlsx", "sheets": {"作業手順書": {"rows": [{"r": 2, "c": {"1": "作業手順書"}}, {"r": 3, "c": {"1": "作業名", "2": "部品AとBの組付け作業", "5": "作成者", "6": "佐藤花子"}}, {"r": 4, "c": {"1": "作業開始時間", "2": "2025-10-10 13:50:00", "5": "作業終了時間", "6": "2025-10-10 17:30:00"}}, {"r": 5, "c": {"1": "手順No.", "2": "作業内容", "5": "注意事項", "8": "必要工具", "9": "チェック"}}, {"r": 6, "c": {"1": 1, "2": "部品Aを開梱し、作業台の中央に置く。", "5": "表面のキズや汚れを確認する", "8": "作業手袋"}}, {"r": 7, "c": {"2": "表面の傷、変形、汚れがないか目視で確認する。", "8": "エアブロー缶"}}, {"r": 8, "c": {"8": "清掃用クロス"}}, {"r": 11, "c": {"1": 2, "2": "部品Bのピン位置と向きを確認し、", "5": "ピンの向きが逆でないか注意する。", "8": "ピンセット"}}, {"r": 12, "c": {"2": "部品Aとの適合を確認する。", "8": "保護マット"}}, {"r": 16, "c": {"1": 3, "2": "部品Bのピンを部品Aの穴に慎重に挿入する。", "5": "抵抗がある場合は無理に押し込まず、位置をやり直す。", "8": "位置合わせ治具"}}, {"r": 17, "c": {"8": "ゴムハンマー"}}, {"r": 21, "c": {"1": 4, "2": "仮固定後、指定のネジをセットして仮締めする。", "5": "ネジの締め忘れや過締めがないか確認する。", "8": "トルクレンチ"}}, {"r": 22, "c": {"2": "トルクレンチで規定値に合わせて本締めを行う。"}}, {"r": 26, "c": {"1": 5, "2": "組立品を検査装置にセットし、動作確認を実施する。"}}, {"r": 27, "c": {"2": "検査結果を検査シートに記録し、合否を明記する。"}}, {"r": 28, "c": {"2": "合格品は梱包して所定の保管場所へ移動する。"}}], "table_candidates": ["B2:C7", "F3:G6"]}, "空白": {"rows": [{"r": 2, "c": {"1": "作業手順書"}}, {"r": 3, "c": {"1": "作業名", "5": "作成者"}}, {"r": 4, "c": {"1": "作業開始時間", "5": "作業終了時間"}}, {"r": 5, "c": {"1": "手順", "2": "作業内容", "5": "メモ(必要なツールや材料、注意事項)", "8": "チェック"}}, {"r": 6, "c": {"1": 1, "8": "False"}}, {"r": 11, "c": {"1": 2, "8": "False"}}, {"r": 16, "c": {"1": 3, "8": "False"}}, {"r": 21, "c": {"1": 4, "8": "False"}}, {"r": 26, "c": {"1": 5, "8": "False"}}]}}}
LLMがJSONから内容を推論したものがこちら
## 作業手順書
### 基本情報
- **作業名:** 部品AとBの組付け作業
- **作成者:** 佐藤花子
- **作業開始時間:** 2025-10-10 13:50:00
- **作業終了時間:** 2025-10-10 17:30:00
---
### 手順一覧
| **手順No.** | **作業内容** | **注意事項** | **必要工具** | **チェック** |
|---:|---|---|---|---|
| 1 | 部品Aを開梱し、作業台の中央に置く。; 表面の傷、変形、汚れがないか目視で確認する。 | 表面のキズや汚れを確認する | 作業手袋; エアブロー缶; 清掃用クロス | False |
| 2 | 部品Bのピン位置と向きを確認し、部品Aとの適合を確認する。 | ピンの向きが逆でないか注意する。 | ピンセット; 保護マット | False |
| 3 | 部品Bのピンを部品Aの穴に慎重に挿入する。 | 抵抗がある場合は無理に押し込まず、位置をやり直す。 | 位置合わせ治具; ゴムハンマー | False |
| 4 | 仮固定後、指定のネジをセットして仮締めする。; トルクレンチで規定値に合わせて本締めを行う。 | ネジの締め忘れや過締めがないか確認する。 | トルクレンチ | False |
| 5 | 組立品を検査装置にセットし、動作確認を実施する。; 検査結果を検査シートに記録し、合否を明記する。; 合格品は梱包して所定の保管場所へ移動する。 | | | False |
こちらは図形などが一切ないので難なく理解できていますね。
JSONだけでもここまで認識精度を向上できるので、これに加えてPDFや画像化したものを同時にLLMに渡せばほぼ完璧にExcel文書を理解させることができます。
既存手法との比較
| 手法 | セル値 | 図形/矢印 | グラフ構造 | 表推定 | コスト | 環境 |
|---|---|---|---|---|---|---|
| pandas / openpyxl | ◎ | ✕ | ✕ | △ | 無料 | クロスOS |
| OCR / VLM | △ | △ | △ | △ | 高 | GPU/Cloud |
| Azure Document Intelligence | △ | △ | △ | △ | 高 | Cloud |
| ExStruct | ◎ | ◎ | ◎ | ◎ | 無料 | Windows+Excel |
RAGでの活用シーン
ExStruct は サーバー常駐で使うライブラリではありません。
Excel が存在する Windows 環境でオフライン前処理として実行し、
生成された構造化データを RAG / 検索基盤に投入する用途を想定しています。
もちろん欠点もある
良いとこだけアピールしましたが、もちろん欠点もあります。
基本的に高度な抽出はxlwingsを通じたCOM参照が必要なので、動作環境はWindows+Excelが必須です。MacやLinuxでは無理です。(一応、Lightモードとしてopenpyxlだけでの簡易構造化フォールバックも用意はしてます)
しかし、Excel文書に困っている現場は基本的にWindows+Excelの環境が揃っていることが殆どなので、ここは割り切って実装しました。
ExcelのOpenXML解析を自力実装すれば脱COMできますが、相当しんどいと思うので今はこれで行こうと思います。たぶん先にビッグテックがExcel文書の解析技術を開発してくれると思う
結果
このExcel解析→徹底的な構造化というAIを使わない泥臭いやり方ですが、結果的にLLMのExcel文書に対する理解度がかなり向上しました。
この構造化処理だけでも
-
LLMへ社内文書の質問
-
RAGデータ蓄積の前処理にかかる工数
などで効果を実感しています。
Azureの Document Intelligence のようなドキュメント解析に特化したクラウドサービスも登場しており、今後Excel文書解析技術は発展していくでしょう。しかし、あまり予算を取れない企業などでは、このようなアプローチも有効ではないでしょうか。
(おまけ)ライブラリ開発話
ライブラリ作成はuv × Codex × ChatGPTで行いました。
| ライブラリ | 用途 |
|---|---|
| xlwings | Excel COM参照するためのAPIを提供 |
| pydantic | データモデルの定義 |
| openpyxl, pandas | 高速なセルの走査 |
| scipy | 表検出アルゴリズムに使用 |
| pytest, pytest-mock | テスト |
| ruff, mypy | リンター・フォーマッター |
| mkdocs-material, mkdocstrings-python | mdファイル、docstringからAPIリファレンス静的サイト自動生成 |
内部実装
データモデルの構造は以下のような感じです
正直ひたすらCOM参照してルールベース解析→データ抽出して徹底的に構造化してるだけです。
以下は図形オブジェクト解析のコード。図形番号とMicrosoftが公開している図形番号に基づくチャート種類を判定したり、矢印系のオブジェクトは角度と方向をプロパティから算出したりしてます。
こだわったところ
この構造化データはLLMに入力することを前提として作っているため、コンテキスト量やトークン量の問題は必須です。
LLMと推論テストを何度もして構造化データの構成を練ったり、ユーザー側で各種データ出力項目を自由に調整できるようにしました。
from exstruct import ExStructEngine, StructOptions, OutputOptions, FormatOptions, FilterOptions, DestinationOptions
engine = ExStructEngine(
options=StructOptions(mode="verbose"), # verbose ではハイパーリンクがデフォルトで含まれる
output=OutputOptions(
format=FormatOptions(pretty=True),
filters=FilterOptions(include_shapes=False), # 図形を出力から除外
destinations=DestinationOptions(sheets_dir=Path("out_sheets")), # シートごとに保存
),
)
wb2 = engine.extract("input.xlsx")
engine.export(wb2, Path("out_filtered.json")) # フィルタ適用後の出力
MkDocs × AIエージェントの組み合わせが最高だった
ライブラリにはAPIリファレンスがあると嬉しいですよね。ということで今回はGitHub PagesにAPIリファレンスのサイトもデプロイしています。
今回はMkDocs-Materialというライブラリを使用しました。
こちらはpip installして簡単なyamlの設定とmdファイルを作ればコマンド一発でサイトが生成されるという静的サイトジェネレーターです。
使い方
インストール
pip install mkdocs-material
また、mkdocstrings-pythonというライブラリを使用すると、プログラムのdocstringから自動でドキュメントを生成してくれるので、APIリファレンス部分のドキュメントはワイヤーフレームだけ組めば後は特に何もする必要もありません。
こちらもインストール
pip install mkdocstrings-python
MkDocsはデフォルトでdocs/ディレクトリ配下のmdファイルをページとして扱います。ここにサイトページをmdファイルで作成してください。
次にmkdocs.ymlを作成して以下のようにmkdocstrings-python拡張をpluginsに設定したり、ページ階層を定義します。
あとは
mkdocs build
すれば自動で静的サイトが生成されます。
しかもGithub Actionsを組み合わせれば、リポジトリプッシュと同時にGitHub Pagesにデプロイできるお手軽仕様です。
以下のような簡単なワークフローだけで完了です。
これとCodexを組み合わせると
-
Codexで実装(docstringをしっかり書かせる)
-
リポジトリプッシュ
-
CIで自動でリファレンスを更新完了
みたいな感じで実装のついでにAPIリファレンスサイトも作れちゃいます。
APIリファレンスがあるだけで何か本格的なライブラリ感出てきますよね(小並感)
今後のロードマップ(検討中)
- セル背景色抽出(ガントチャート対応)
- SmartArt解析対応
- 図形グループの意味的クラスタリング
- JSON → Excel 再構成(部分的)
(魔境)OpenXML解析→脱COM依存でサーバー対応
おわりに
今回は厄介なExcel文書をどうにかこうにか解析構造化してLLMのExcelに対する理解度を爆上げ👆️させるアプローチを紹介してみました。
社内ナレッジのDXに四苦八苦している製造業などのご参考になれば幸いです。
それではまた👋
補足リンク
- TOON形式についての参考記事
- Azure Document Intelligence
Discussion