支店ごとや月ごとに届くCSVやExcelを、毎回コピー&ペーストで1つにまとめていませんか?正直、あの作業って地味に疲れますよね。
- 列の名前が「日付」「注文日」とファイルごとに違って、貼り付けるたびに直している
- 同じ注文が複数のファイルに入っていて、重複を目で探している
- CSVを開いたら文字化けして、保存し直している
フォルダに入れたCSV(UTF-8・Shift_JIS)とExcelを、列名のゆれをそろえて1つにまとめ、重複を除いて保存するPythonスクリプトを、一緒に作っていきます。手作業なら10分以上かかる作業が、実行1回・数秒になります。
所要時間の目安は約20分です。
全体像:読み込み → 列名をそろえる → 結合 → 重複を除く
| 手順 | やること | 使うもの |
|---|---|---|
| 1 | CSVとExcelを読み込む(文字コードも自動で試す) | read_csv / read_excel |
| 2 | 列名のゆれを正しい名前にそろえる | 対応表(辞書) |
| 3 | 全ファイルを縦に結合する | concat |
| 4 | 重複する行を除く | drop_duplicates |
| 5 | CSVに保存する | to_csv |
毎朝のCSVを集計する方法は、こちらの記事で扱っています。今回は「複数ファイルを1つにまとめる」ところに絞って、ゆっくり進めていきます。
Pythonで毎朝のCSV集計を自動化する(pandas+定期実行)
準備:ライブラリと、練習用のフォルダ
1. ライブラリを入れる
ターミナル(Windowsは PowerShell)を開いて、実行してみてください。
- 1
pip install pandas openpyxlopenpyxl は、Excel(.xlsx)を読み書きするために使います。この記事のコードは、Python 3.13・pandas 3.0.6 で実際に動かして確認しています。
2. 練習用のダミーデータを用意する
作業用のフォルダに data フォルダを作り、次の3つのファイルを入れます。仕事でよくある「列名のゆれ」「文字コードの違い」「重複」を、わざと入れてあります。
orders_tokyo.csv(UTF-8)
- 1
- 2
- 3
- 4
注文日,氏名,メール,商品,数量
2026/10/01,山田 太郎,taro@example.com,ノートA5,2
2026/10/02,佐藤 花子,hanako@example.com,ペン3本セット,1
2026/10/03,鈴木 一郎,ichiro@example.com,ノートA5,3orders_osaka.csv(Shift_JIS。Excelで「CSV」として保存したものと同じ形式)
- 1
- 2
- 3
- 4
日付,お名前,Email,商品名,個数
2026-10-01,山田 太郎, Taro@Example.com ,ノートA5,2
2026-10-04,高橋 次郎,jiro@example.com,付箋セット,5
2026-10-05,伊藤 美咲,misaki@example.com,ペン3本セット,2orders_web.xlsx(Excel。列は 注文日/名前/メールアドレス/商品/数量。「数量」の後ろに空白が混ざっています)
| 注文日 | 名前 | メールアドレス | 商品 | 数量 |
|---|---|---|---|---|
| 2026-10-02 | 佐藤 花子 | [email protected] | ペン3本セット | 1 |
| 2026-10-06 | 渡辺 健 | [email protected] | 付箋セット | 1 |
| 2026-10-06 | 渡辺 健 | [email protected] | 付箋セット | 1 |
手順1:スクリプトを作る
merge_orders.py という名前で、data フォルダと同じ場所に保存します。コードは長めに見えますが、まずはそのままコピペして動かすところから始めましょう。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
"""data フォルダ内の CSV / Excel を1つにまとめ、重複を除いて保存するスクリプト"""
import unicodedata
from pathlib import Path
import pandas as pd
# ===== 設定 =====
INPUT_DIR = Path("data") # まとめたいファイルを入れるフォルダ
OUTPUT_FILE = Path("merged_orders.csv")
# 列名ゆれの対応表:「正しい列名: [ゆれの候補]」
COLUMN_ALIASES = {
"注文日": ["注文日", "日付", "購入日"],
"氏名": ["氏名", "お名前", "名前"],
"メール": ["メール", "メールアドレス", "email", "e-mail"],
"商品": ["商品", "商品名"],
"数量": ["数量", "個数", "数"],
}
# 重複とみなす条件(この3列がすべて同じ行は重複)
DUPLICATE_KEYS = ["注文日", "メール", "商品"]
# =================
# 「ゆれの候補 → 正しい列名」の逆引き表を作る
ALIAS_TO_STANDARD = {
alias.lower(): standard
for standard, aliases in COLUMN_ALIASES.items()
for alias in aliases
}
def clean_name(name) -> str:
"""列名の全角・半角や前後の空白をそろえる"""
return unicodedata.normalize("NFKC", str(name)).strip().lower()
def read_file(path: Path) -> pd.DataFrame:
"""CSV(UTF-8 / Shift_JIS)とExcelを読み込む"""
if path.suffix.lower() == ".csv":
for encoding in ("utf-8-sig", "cp932"):
try:
return pd.read_csv(path, encoding=encoding)
except UnicodeDecodeError:
continue
raise ValueError(f"{path.name}: 文字コードを判別できませんでした")
return pd.read_excel(path) # 先頭のシートを読み込む
def load_one(path: Path) -> pd.DataFrame:
df = read_file(path)
# 1) 列名をそろえる
df.columns = [ALIAS_TO_STANDARD.get(clean_name(c), str(c).strip()) for c in df.columns]
# 2) 必要な列が足りなければ、ファイル名つきで知らせる
missing = [c for c in COLUMN_ALIASES if c not in df.columns]
if missing:
raise ValueError(f"{path.name}: 列が見つかりません {missing} / 実際の列: {list(df.columns)}")
# 3) 値をそろえる(日付・メールの表記ゆれ)
df["注文日"] = pd.to_datetime(df["注文日"], errors="coerce")
df["メール"] = df["メール"].astype(str).str.strip().str.lower()
df["氏名"] = df["氏名"].astype(str).str.strip()
df = df[list(COLUMN_ALIASES)].copy()
df["元ファイル"] = path.name
return df
def main() -> None:
files = sorted(
p for p in INPUT_DIR.iterdir()
if p.suffix.lower() in {".csv", ".xlsx"} and not p.name.startswith(("~$", "."))
)
if not files:
print(f"{INPUT_DIR} にCSV/Excelがありません")
return
frames = [load_one(p) for p in files]
for p, f in zip(files, frames):
print(f"読み込み:{p.name}({len(f)}行)")
merged = pd.concat(frames, ignore_index=True)
before = len(merged)
# 日付が読めなかった行は確認用に知らせる
bad_dates = merged["注文日"].isna().sum()
# 重複を除く(最初に出てきた行を残す)
merged = merged.drop_duplicates(subset=DUPLICATE_KEYS, keep="first")
merged = merged.sort_values("注文日", kind="stable").reset_index(drop=True)
merged.to_csv(OUTPUT_FILE, index=False, encoding="utf-8-sig") # Excelで開いても文字化けしない
print(f"--- 結合:{before}行 → 重複を除いて {len(merged)}行({before - len(merged)}行削除) ---")
if bad_dates:
print(f"※日付を読み取れなかった行が {bad_dates} 行あります")
print(f"保存しました:{OUTPUT_FILE}")
if __name__ == "__main__":
main()コードの読み方(3か所だけ)
全部を理解しなくても大丈夫です。気にしてほしいのは、この3か所です。
1. COLUMN_ALIASES:列名のゆれを登録する
「正しい列名:ゆれの候補」の対応表です。新しいゆれが見つかったら、ここに足すだけでOKです。列名は全角・半角、大文字・小文字、前後の空白をそろえてから比べるので、「Email」「email」「 e-mail 」も同じ扱いになります。
2. read_file:文字コードを順番に試す
CSVはまず UTF-8(utf-8-sig)で読み、失敗したら Shift_JIS(cp932)で読みます。Excelで保存したCSVは Shift_JIS のことが多いため、この順番にしています。
3. drop_duplicates:重複の条件を決める
DUPLICATE_KEYS に指定した列がすべて同じ行を、重複とみなします。今回は「注文日・メール・商品」です。ファイル名順に並べて結合しているので、先に出てきた行が残ります。
手順2:実行して結果を確認する
merge_orders.py があるフォルダで、次のコマンドを実行します(Macは python3)。
- 1
python merge_orders.py実際に実行してみた結果は、こんな感じです。
- 1
- 2
- 3
- 4
- 5
読み込み:orders_osaka.csv(3行)
読み込み:orders_tokyo.csv(3行)
読み込み:orders_web.xlsx(3行)
--- 結合:9行 → 重複を除いて 6行(3行削除) ---
保存しました:merged_orders.csv9行が6行になりました!削除された3行は、山田さんの注文(東京と大阪の両方にある)、佐藤さんの注文(東京とExcelの両方にある)、渡辺さんの注文(Excel内の二重入力)です。山田さんは、メールの大文字・空白をそろえたことで、同じ注文として判定されています。
保存された merged_orders.csv の中身はこうなります。
| 注文日 | 氏名 | メール | 商品 | 数量 | 元ファイル |
|---|---|---|---|---|---|
| 2026-10-01 | 山田 太郎 | [email protected] | ノートA5 | 2 | orders_osaka.csv |
| 2026-10-02 | 佐藤 花子 | [email protected] | ペン3本セット | 1 | orders_tokyo.csv |
| 2026-10-03 | 鈴木 一郎 | [email protected] | ノートA5 | 3 | orders_tokyo.csv |
| 2026-10-04 | 高橋 次郎 | [email protected] | 付箋セット | 5 | orders_osaka.csv |
| 2026-10-05 | 伊藤 美咲 | [email protected] | ペン3本セット | 2 | orders_osaka.csv |
| 2026-10-06 | 渡辺 健 | [email protected] | 付箋セット | 1 | orders_web.xlsx |
「元ファイル」列があるので、どの行がどのファイルから来たのかを後から追えるので、ちょっと安心です。保存時に utf-8-sig を指定しているので、Excelで開いても文字化けしません。
自分のデータに合わせて変える
| 変えたいこと | 変更する場所 |
|---|---|
| まとめたいフォルダ | INPUT_DIR |
| 保存するファイル名 | OUTPUT_FILE |
| 列名のゆれを増やす | COLUMN_ALIASES に候補を足す |
| 重複とみなす条件 | DUPLICATE_KEYS(例:注文番号の列だけ) |
| 列そのものを増やす | COLUMN_ALIASES に新しい列を追加する |
つまずきやすい点
エラーが出ても慌てなくて大丈夫です。まずはこの表を見てみてください。
| 症状 | 原因 | 対処 |
|---|---|---|
列が見つかりません というエラーが出る | 列名のゆれが対応表にない | エラーに出るファイル名と実際の列名を見て、COLUMN_ALIASES に足す |
ModuleNotFoundError: No module named 'openpyxl' | Excelを読むライブラリが未導入 | pip install openpyxl を実行する |
| 古い形式の .xls が読めない | このスクリプトは .xlsx と .csv だけ対象 | Excelで .xlsx に保存し直す |
| 日付が空欄になる | 日付として読み取れない文字が入っている | 実行時に「日付を読み取れなかった行」の件数が表示されるので、元ファイルを確認する |
| 重複が消えない | 比べる列の表記ゆれ(空白・大文字小文字など) | 比較する列を、読み込み時にそろえる(メールのように) |
| Excelのファイルを開いたまま実行して失敗する | 一時ファイル(~$ で始まる)が混ざる | 閉じてから実行する。~$ のファイルはスクリプト側で除外済み |
まとめ
- 列名のゆれは、対応表(辞書)に集めると、あとから足しやすい
- 文字コードは UTF-8 → Shift_JIS の順に試し、保存は
utf-8-sigにしておく - 重複の条件(どの列が同じなら重複か)は、自分のデータに合わせて決める
次の一歩は、手元のCSVを2〜3個コピーして data フォルダに入れ、COLUMN_ALIASES に自分の列名を足して実行してみることです。最初は小さく試すのが、僕のおすすめです。
関連する本・記事
ファイルが増えて処理が重くなってきたときの次の一歩として、Pythonを速くする考え方を扱う『爆速Pythonの作り方』もあります。
- Excelからの乗り換えで迷っている方は、こちらの記事もどうぞ:メンバーがExcel VBAの書籍を書いたよ!『動くけど危ないExcel VBA』