
あなたの会社にも、きっとありますよね。
「Excelファイルの中から“この条件に合うデータだけ抽出して”」と言われる地味~な仕事。
たとえばこんな悩み。
-
「毎月ちょっと条件が違うから、関数を毎回直すのがめんどい…」
-
「どの列に条件を入れたか、わからなくなった!」
-
「気づけば10枚以上のExcelを開いてCtrl+F地獄」
――これ、まるで迷路に落ちたエクセル探偵状態。
毎回頑張って関数を修正していたあの日々よ、さようなら!
今回は、ChatGPT×Pythonの力を借りて、「Excelを開かずに条件抽出ができるアプリ」を作ってみました。
しかも、Pythonコードを直接いじる必要はナシ。
列名や条件値は、Excelでカスタムするだけ!
つまり――
「Pythonはむずかしい」と感じていたあなたでも、“Excelの達人感覚”でアプリを使いこなせるというわけです。
Pythonでコードをいじるのはムリゲーだけど、Excelで条件変更できるならやってみたい!という、ぼくのように 大着な 好奇心旺盛な方は、ぜひ読んでみてください。
- 【課題】なぜExcel抽出作業は面倒なのか
- 【解決策】ChatGPT×Pythonで“抽出アプリ”を作るとこう変わる
- 【実演】GUIで条件を入力して抽出してみた!
- 💬 ChatGPTへの依頼プロンプト
- 🐍 ChatGPTが生成したPythonコード
- 📊 サンプルデータを自分で作って試そう!
【課題】なぜExcel抽出作業は面倒なのか

Excelの抽出作業って、一見かんたんそうに見えて、地味にストレスですよね。
フィルターをかけて、並べ替えて、条件を変えて、また元に戻して…。
まるで「ちょっと片付けよう」と思って押し入れを開けたら、気づけば模様替えを始めてた週末のような泥沼です。
しかも、「この列が在庫」「この列が仕入先」「あっ、列順変わってた」
――と、毎回パニック。
そんな時こそ必殺Pythonです。
とはいえ、「コードなんて書けない!」という声もわかります。
だから今回も、ChatGPTにまるっと作ってもらうスタイルでいきましょう。
【解決策】ChatGPT×Pythonで“抽出アプリ”を作るとこう変わる
今回のアプリのすごいところは、ズバリ!
これ、非エンジニアでもPythonアプリを“使う”だけの立場になれるやつです。
現場の声を代弁するならこんな感じ。
【実演】GUIで条件を入力して抽出してみた!

💬 ChatGPTへの依頼プロンプト
今回は、ChatGPTにこんな感じでPythonアプリを依頼しました。
(※このプロンプトは、非エンジニアでもコピペして少し直すだけでOKです)
あなたはPythonのプロエンジニア兼UIデザイナーです。 以下の条件を満たす業務改善アプリを作ってください。 【アプリ概要】 ・ExcelとCSVの両方を読み込み、共通のキー(例:「品番」)で照合。 ・条件は最大5件まで指定可。含む/一致/以上/以下など複数対応。 ・条件と昇順/降順設定をExcelで事前に指定。 ・GUIはtkinterで作成。大きめの文字と見やすいボタン配置。 ・結果表示とは別に、ユーザー条件の上位3件をランキング表示。 ・安全対策:データ削除や上書き処理は行わない。 【UI要件】 ・フォルダ指定、抽出ボタン、結果表示エリア、ランキングエリアを配置。 ・処理状況をメッセージで表示(例:「抽出中…」「完了しました!」)。 ・GUIは日本語ラベルとわかりやすいボタンデザイン。 【スタイル】 ・ttk.Styleを使用し、見やすいストライプ背景。 ・ボタンは角丸で大きめ。背景は落ち着いたグレー基調。
🐍 ChatGPTが生成したPythonコード
ChatGPTが生成してくれたコードはこちら。
コピーボタン付きなので、そのまま貼り付けて動かせます。
# ======================================================= # 📌 Excel/CSV 抽出アプリ(完全版) # - tkinter GUI # - pandas で Excel/CSV を読み込み、条件で抽出 # - 条件は最大5件(Excelから読み込み可能) # - 上位3つの最近使った条件ランキングを表示 # - 抽出結果を CSV 保存(上書き回避) # # 必要なライブラリ: # pip install pandas openpyxl # ======================================================= import tkinter as tk from tkinter import ttk, filedialog, messagebox import pandas as pd import os from datetime import datetime # ------------------------------ # ヘルパー関数 # ------------------------------ def safe_to_numeric(series): """数値比較できるように変換(失敗は NaN)""" return pd.to_numeric(series, errors='coerce') def make_unique_path(path): """既存ファイルがあれば _copy, _copy2 ... を付与して返す""" base, ext = os.path.splitext(path) if not os.path.exists(path): return path i = 1 new_path = f"{base}_copy{ext}" while os.path.exists(new_path): i += 1 new_path = f"{base}_copy{i}{ext}" return new_path def cond_to_string(cond_entries): """条件エントリ群を人が読める文字列にする""" parts = [] for col, val, method in cond_entries: colv = col.get().strip() valv = val.get().strip() meth = method.get().strip() if colv and valv and meth: parts.append(f"{colv} {meth} {valv}") return " | ".join(parts) if parts else "(条件なし)" # ------------------------------ # メインアプリ # ------------------------------ class ExcelFilterApp: def __init__(self, root): self.root = root self.root.title("🔎 Excel/CSV 抽出アプリ(条件カスタム可)") self.root.geometry("980x640") self.style = ttk.Style() try: self.style.theme_use("clam") except: pass self.style.configure("TButton", font=("Meiryo", 11)) self.style.configure("TLabel", font=("Meiryo", 11)) self.style.configure("Treeview", rowheight=26, font=("Meiryo", 10)) self.style.configure("TFrame", background="#f7f7f7") # データ関連 self.data = None self.source_path = None # 最近の条件(メモリ内保持、再起動で消える) self.recent_conditions = [] self.create_widgets() def create_widgets(self): # --- 上部ボタン群 --- top_frame = ttk.Frame(self.root) top_frame.pack(fill="x", padx=10, pady=8) ttk.Button(top_frame, text="📂 ファイルを選択", command=self.load_file).pack(side="left", padx=6) self.lbl_file = ttk.Label(top_frame, text="未選択", width=50) self.lbl_file.pack(side="left", padx=6) ttk.Button(top_frame, text="⚙ 条件を読み込み (Excel)", command=self.load_conditions).pack(side="left", padx=6) ttk.Button(top_frame, text="🔍 抽出開始", command=self.filter_data).pack(side="left", padx=6) ttk.Button(top_frame, text="💾 結果をCSV保存", command=self.save_csv).pack(side="left", padx=6) ttk.Button(top_frame, text="❌ 閉じる", command=self.root.destroy).pack(side="right", padx=6) # --- メイン中央:条件エリアと結果エリアを左右で配置 --- main_pane = ttk.Panedwindow(self.root, orient=tk.HORIZONTAL) main_pane.pack(fill="both", expand=True, padx=10, pady=6) # 左:条件エリア cond_frame = ttk.Labelframe(main_pane, text="抽出条件(最大5件)") cond_frame.config(width=380) main_pane.add(cond_frame, weight=1) self.condition_entries = [] for i in range(5): row = ttk.Frame(cond_frame) row.pack(fill="x", pady=4, padx=6) ttk.Label(row, text=f"{i+1}").pack(side="left", padx=(0,6)) col_entry = ttk.Entry(row, width=16) col_entry.pack(side="left", padx=4) col_entry.insert(0, "") # placeholder val_entry = ttk.Entry(row, width=14) val_entry.pack(side="left", padx=4) cmb = ttk.Combobox(row, values=["完全一致", "部分一致", "以上", "以下", "除外"], width=10) cmb.pack(side="left", padx=4) cmb.set("部分一致") self.condition_entries.append((col_entry, val_entry, cmb)) # ソート指定 sort_row = ttk.Frame(cond_frame) sort_row.pack(fill="x", pady=8, padx=6) ttk.Label(sort_row, text="並べ替え列").pack(side="left", padx=4) self.cmb_sort_col = ttk.Combobox(sort_row, values=[], width=18) self.cmb_sort_col.pack(side="left", padx=4) self.sort_var = tk.StringVar(value="昇順") ttk.Radiobutton(sort_row, text="昇順", variable=self.sort_var, value="昇順").pack(side="left", padx=4) ttk.Radiobutton(sort_row, text="降順", variable=self.sort_var, value="降順").pack(side="left", padx=4) # 最近の条件ランキング rank_frame = ttk.Labelframe(cond_frame, text="📈 最近の条件ランキング(上位3)") rank_frame.pack(fill="x", padx=6, pady=10) self.rank_labels = [] for i in range(3): lbl = ttk.Label(rank_frame, text=f"{i+1}. (なし)", wraplength=300) lbl.pack(anchor="w", padx=6, pady=2) self.rank_labels.append(lbl) # 右:結果表示エリア result_frame = ttk.Labelframe(main_pane, text="抽出結果") result_frame.config(width=600) main_pane.add(result_frame, weight=3) # Treeview self.tree = ttk.Treeview(result_frame, columns=("dummy",), show="headings") self.tree.pack(fill="both", expand=True, padx=6, pady=6) # ストライプのためのタグ self.tree.tag_configure("odd", background="#ffffff") self.tree.tag_configure("even", background="#f2f6ff") # ステータスバー self.status_var = tk.StringVar(value="準備完了") status_bar = ttk.Label(self.root, textvariable=self.status_var, relief="sunken", anchor="w") status_bar.pack(fill="x", padx=10, pady=(0,6)) # ------------------------------ # ファイル読み込み # ------------------------------ def load_file(self): file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx"), ("CSV files", "*.csv")]) if not file_path: return try: ext = os.path.splitext(file_path)[1].lower() if ext == ".csv": df = pd.read_csv(file_path, dtype=str) else: df = pd.read_excel(file_path, dtype=str) # 読み込んだ全列を文字列扱いで保持(比較時に数値化) self.data = df self.source_path = file_path self.lbl_file.config(text=os.path.basename(file_path)) self.status_var.set(f"読み込み完了: {os.path.basename(file_path)} ({len(df)}行)") # Treeview の列候補を更新 cols = list(self.data.columns) self.cmb_sort_col['values'] = cols # ツリービューを初期表示しておく self.show_result(self.data.head(200)) except Exception as e: messagebox.showerror("読み込みエラー", f"ファイルの読み込みに失敗しました。\n{e}") self.status_var.set("読み込み失敗") # ------------------------------ # 条件設定 Excel 読み込み # ------------------------------ def load_conditions(self): cond_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")]) if not cond_path: return try: cond_df = pd.read_excel(cond_path, dtype=str) # 期待列: 列名, 条件値, 一致方法 (オプション: 種別) for i, (_, row) in enumerate(cond_df.iterrows()): if i >= 5: break col = str(row.get("列名", "")).strip() val = str(row.get("条件値", "")).strip() method = str(row.get("一致方法", "")).strip() # 値を GUI にセット self.condition_entries[i][0].delete(0, tk.END) self.condition_entries[i][0].insert(0, col) self.condition_entries[i][1].delete(0, tk.END) self.condition_entries[i][1].insert(0, val) if method: try: self.condition_entries[i][2].set(method) except: pass messagebox.showinfo("条件読み込み", "条件設定を読み込みました。") except Exception as e: messagebox.showerror("条件読み込みエラー", f"条件ファイルの読み込みに失敗しました。\n{e}") # ------------------------------ # データ抽出のコア # ------------------------------ def filter_data(self): if self.data is None: messagebox.showwarning("未選択", "先にExcelまたはCSVファイルを読み込んでください。") return df = self.data.copy() # 条件順に適用 used_conditions = [] try: for col_entry, val_entry, cmb in self.condition_entries: col = col_entry.get().strip() val = val_entry.get().strip() method = cmb.get().strip() if not col or not val or not method: continue # 空はスキップ # 列存在チェック if col not in df.columns: messagebox.showwarning("列エラー", f"列「{col}」がデータに存在しません。") return # 部分一致 or 完全一致 or 数値比較 or 除外 if method == "完全一致": df = df[df[col].astype(str) == val] elif method == "部分一致": df = df[df[col].astype(str).str.contains(val, na=False)] elif method in ("以上", "以下"): # 数値に変換して比較(変換できない場合は警告) col_num = safe_to_numeric(df[col]) try: val_num = float(val) except: messagebox.showwarning("比較エラー", f"数値比較の条件値 '{val}' が数値として解釈できません。") return if method == "以上": df = df[col_num >= val_num] else: df = df[col_num <= val_num] elif method == "除外": # 除外は部分一致と完全一致の両方に対応(ここでは部分一致除外) df = df[~df[col].astype(str).str.contains(val, na=False)] else: # 未知の方法はスキップ continue used_conditions.append((col, method, val)) # 並べ替え sort_col = self.cmb_sort_col.get().strip() if sort_col: ascending = True if self.sort_var.get() == "昇順" else False # try numeric sort first try: df_sort = df.copy() df_sort[sort_col + "_num"] = safe_to_numeric(df_sort[sort_col]) if df_sort[sort_col + "_num"].notna().any(): df = df.sort_values(by=sort_col + "_num", ascending=ascending) df = df.drop(columns=[sort_col + "_num"]) else: df = df.sort_values(by=sort_col, ascending=ascending) except Exception: try: df = df.sort_values(by=sort_col, ascending=ascending) except Exception: pass # 結果表示 self.show_result(df) cnt = len(df) self.status_var.set(f"抽出完了:{cnt} 件") messagebox.showinfo("抽出完了", f"{cnt} 件が抽出されました。") # 最近条件ランキングを更新(先頭3) cond_text = cond_to_string(self.condition_entries) if cond_text and cond_text != "(条件なし)": # recent_conditions に追加し、頻度で上位3を表示 self.recent_conditions.insert(0, cond_text) # 重複削除かつ順序維持(先頭優先) seen = [] uniq = [] for c in self.recent_conditions: if c not in seen: uniq.append(c) seen.append(c) self.recent_conditions = uniq[:20] # 最大20保持 for i in range(3): if i < len(self.recent_conditions): self.rank_labels[i].config(text=f"{i+1}. {self.recent_conditions[i]}") else: self.rank_labels[i].config(text=f"{i+1}. (なし)") except Exception as e: messagebox.showerror("抽出エラー", f"抽出処理中にエラーが発生しました。\n{e}") self.status_var.set("抽出失敗") # ------------------------------ # ツリービュー表示 # ------------------------------ def show_result(self, df): # 既存削除 for item in self.tree.get_children(): self.tree.delete(item) if df is None or len(df) == 0: # カラムだけセット self.tree["columns"] = ("なし",) self.tree.heading("なし", text="(該当データなし)") return cols = list(df.columns) self.tree["columns"] = cols for col in cols: self.tree.heading(col, text=col) # 幅自動調整(簡易) self.tree.column(col, width=120, anchor="center") # 行データ挿入 for i, (_, row) in enumerate(df.iterrows()): tag = "even" if i % 2 == 0 else "odd" vals = ["" if pd.isna(v) else str(v) for v in row.tolist()] self.tree.insert("", "end", values=vals, tags=(tag,)) # ------------------------------ # CSV保存 # ------------------------------ def save_csv(self): # collect current tree data items = self.tree.get_children() if not items: messagebox.showwarning("保存エラー", "保存する抽出結果がありません。") return file_path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV files", "*.csv")]) if not file_path: return try: df = pd.DataFrame([self.tree.item(it)["values"] for it in items], columns=self.tree["columns"]) save_path = make_unique_path(file_path) df.to_csv(save_path, index=False, encoding="utf-8-sig") messagebox.showinfo("保存完了", f"結果を {os.path.basename(save_path)} に保存しました。") except Exception as e: messagebox.showerror("保存エラー", f"CSV保存に失敗しました。\n{e}") # ------------------------------ # 実行 # ------------------------------ if __name__ == "__main__": root = tk.Tk() app = ExcelFilterApp(root) root.mainloop()
📊 サンプルデータを自分で作って試そう!
あなたのPCでもアプリを検証できるように、サンプルデータを自動生成するPythonコードを用意しました!
import pandas as pd import random from datetime import datetime, timedelta import os # ============================== # 📦 サンプルデータ生成スクリプト # ============================== output_dir = os.path.expanduser("~/Desktop") os.makedirs(output_dir, exist_ok=True) products = ["ボルトM8", "ナットM6", "ワッシャーS", "ベルトB", "モーターA", "パッキンF", "シャフトG", "ホースH", "ギアI", "プレートJ"] suppliers = ["田中商事", "鈴木製作所", "ABC金属", "高橋工業", "オオタ商会"] records = [] for _ in range(50): p = random.choice(products) s = random.choice(suppliers) q = random.randint(1, 100) u = random.randint(100, 5000) d = (datetime.today() - timedelta(days=random.randint(0, 120))).strftime("%Y-%m-%d") records.append([p, s, q, u, q*u, d]) df = pd.DataFrame(records, columns=["品番", "仕入先", "数量", "単価", "金額", "入庫日"]) excel_path = os.path.join(output_dir, "sample_inventory_data.xlsx") csv_path = os.path.join(output_dir, "sample_inventory_data.csv") df.to_excel(excel_path, index=False, engine="openpyxl") df.to_csv(csv_path, index=False, encoding="utf-8-sig") cond_df = pd.DataFrame({ "列名": ["仕入先", "数量", "単価"], "条件値": ["田中商事", "50", "1000"], "一致方法": ["完全一致", "以上", "以下"] }) cond_path = os.path.join(output_dir, "条件設定.xlsx") cond_df.to_excel(cond_path, index=False, engine="openpyxl") print("✅ サンプルファイルをデスクトップに出力しました!") print(f"・{excel_path}") print(f"・{csv_path}") print(f"・{cond_path}")
🧾 出力されるファイル
| ファイル名 | 内容 | 用途 |
|---|---|---|
| sample_inventory_data.xlsx | メインの在庫データ | 読込対象(Excel) |
| sample_inventory_data.csv | 同データのCSV版 | 読込対象(CSV) |
| 条件設定.xlsx | 抽出条件リスト | アプリのデフォルト条件 |
GUIでは、列名・条件値・一致方法を最大5件まで入力できます。除外条件も同様に設定可能にしました。
さらに右側には「最近使った条件ランキング」が表示され、「そうそう、前回これ使った!」と再利用がしやすい設計です。
「抽出スタート」ボタンを押すと、条件に合った行だけがツリービューにずらっと表示され、結果をCSVとして保存することも可能です。
Pythonを知らなくても、“ちょっと便利な業務用ツール”を自分で動かせる――
そんな体験は、まさに生成AI時代の醍醐味ですね。
【実演】実際のアプリ画面(GUI)と動作検証

VSCodeにコピペしたプログラムを実行してみると…ご覧のとおり、ちゃんとアプリ画面が表示されました。依頼した条件のボタンは、一通り並んでいるようです。
では、左上の「ファイルを選択」から、読み込ませるためのサンプルExcelファイルを指定していきましょう。

Excelファイルを読み込んでくれました。あとは、これに条件指定してフィルタリングしてきます。今回は「"品番"を"ギア"」さらに「"単価"を"1000以上"」としてみましょう。

「ヘイ、お待ち!」と言わんばかりに、秒で結果が表示されました。条件を変えて「抽出開始」すれば、ほかのパターンもできます。
今回のサンプルはとても簡素なデータですが、実際の職場・現場ではもっと多くのデータを蓄積している場合もあるはずです。
蓄積したデータ数が多いほど、「手間<アプリ時短」が顕著で効果が大きいはず。
繰り返しますが、このアプリはExcelで編集できるのが特徴なので、誰でも使いやすいんですね。
興味のある方はぜひ、列名や指定条件の単語などを応用した「自分専用」としてお試しください。
【評価】Excelでカスタムできる仕組みが神すぎた話
普通なら、抽出条件を変えるたびにPythonコードを修正しなきゃいけません。
でも今回は、「条件設定.xlsx」に条件を書き込むだけ。
次に起動したとき、その設定がGUIに自動で反映されます。
たとえばこのように書くだけでOKです:
| 列名 | 条件値 | 一致方法 | 種別(抽出/除外) | 並べ替え方向 |
|---|---|---|---|---|
| 型式 | AB- | 部分一致 | 抽出 | 昇順 |
| 在庫数 | 10 | 以下 | 抽出 | |
| 顧客名 | 田中商事 | 完全一致 | 除外 |
これなら、Pythonを知らなくても設定ファイルをExcelで編集できるので、いわば「ノーコードでPythonアプリを動かす感覚」です。
【注意点とコツ】AIに任せすぎないための3か条

AIが生成したコードを使うときに注意したいのは次の3点です。
① 会社のルールに沿って使う
個人情報や社外秘データはAIにアップロードしない。
ChatGPTは賢いけど、守秘義務の神様ではないのです。
② 最終チェックは必ず人間が行う
抽出ミスや条件の誤設定がないか、最終確認は人の目で。
AIを過信しすぎると、「あれ、数が合わないぞ?」地獄が待ってます。
③ 生成AIは“相談役”として使う
「AIが全部やってくれる」ではなく、
「AIに頼って自分の判断を早める」くらいがちょうどいい。
【まとめ】 ChatGPT×Pythonで作る「条件を自由に変えられるExcel抽出アプリ」

今回は、「Excelの条件を自由に変えられる抽出アプリ」をChatGPTとPythonで作る方法を紹介しました。
関数に埋もれて頭を抱えていたぼくの同志も、アプリひとつで“条件を変えてクリックするだけ”。
顕在ニーズは「作業の効率化」ですが、潜在ニーズは「自分の手でアプリをカスタマイズできる」こと。
Python×ChatGPTを使えば、非エンジニアでも自分専用ツールを作る時代なんです。
「Pythonコード?Excelで設定できるなら、なんだか今日いける気がする!」
そう思った今が、第一歩を踏み出すチャンスです。
あると思います。
↓フォローは24時間365日受付中!
🚀 Xでフォローする(@gen_ai_note)
【ちょいコード活用まとめ】生成AI×業務活用×非エンジニアの奮闘記|えーやい実験室トップへ