「店舗ごとのCSVを、毎月1つずつ開いて貼り付けている」
「全部まとめたつもりなのに、どれか1つが抜けていた」
同じ列のCSVなら、フォルダに集めて、まとめて読み込む方法があります。今回はPythonで、複数のCSVを1つにつなげる手順を紹介します。
元のファイル名も残すので、「この行はどこから来た?」を後でたどれます。ファイルごとの件数も確認できます。まずは、架空のCSV2つをまとめるところから試してみましょう。
1 この方法で扱えるCSVを確認する
今回のサンプルは、次の条件に合わせています。
・1行目が見出しになっている、カンマ区切りのCSV
・すべてのファイルで、見出しの名前と順番が同じ
・見出しに空欄や重複がなく、予約名 _source_file を使っていない
・文字コードはUTF-8。先頭にBOMがあるものも、ないものも対応
・入力は1〜50ファイル、合計1 MiBまで。データは合計10,000件まで
・input フォルダの直下にある、通常のCSVファイルが対象
「縦にデータを足していく」処理です。商品コードを照合して別の表から金額を持ってくる処理や、重複の削除は含みません。Excelの .xlsx ファイルも、そのままでは読めません。
列の並びが違うファイルは、勝手に合わせずに止まります。「名前」と「数量」の列を取り違えて結合しないためです。
【実行に必要なもの】
Python 3.12以降と、Pythonの編集・実行用アプリ「IDLE」を使う前提で説明します。Python本体の用意は必要ですが、pandasなどの追加ライブラリは使いません。
まだPythonが入っていない場合は、Python公式サイトのダウンロード案内を確認して用意してください。会社のパソコンにソフトを入れられない場合は、担当者に確認してから進めます。既にExcelのフォルダー取り込みを使えるなら、その方法で足りる場合もあります。
2 練習用フォルダと、CSVを2つ作る
分かりやすい場所に「csv_merge_practice」というフォルダを作ります。その中に「input」というフォルダを作ってください。
input の中には、今回まとめたいCSVだけを入れます。以前作った結合結果や、同じ内容のコピーを入れると、その分も追加されます。
最初は本番ファイルを使わず、メモ帳などで次の2つを作ります。文字コードはUTF-8、ファイル名はそれぞれ末尾を .csv にして保存してください。「.csv.txt」になっていないかも確認します。
1つ目:01_tokyo.csv
商品コード,商品名,数量
001,りんご,2
002,"みかん,小袋",3
2つ目:02_osaka.csv
商品コード,商品名,数量
003,ぶどう,1
「みかん,小袋」は、商品名の中にカンマがある例です。二重引用符で囲まれているため、1つの商品名として扱います。
3 プログラムを保存する
IDLEを開き、「File」→「New File」で編集画面を出します。次のプログラムをすべてコピーして、「merge_csv.py」という名前で csv_merge_practice フォルダに保存します。
保存場所は input の中ではありません。merge_csv.py と input が横に並ぶ場所です。
コードは、すべての行が左端から始まります。長い行が画面上で折り返されても、自分で途中に改行を追加せず、そのままコピーしてください。「プログラムここから」「ここまで」の文字はコピーに含めません。
【プログラムここから】
import csv
import io
import tempfile
from pathlib import Path
# 1. 入力フォルダと容量を確認
base = Path(__file__).resolve().parent
folder = base / "input"
if not folder.is_dir() or folder.is_symlink(): raise ValueError("通常の input フォルダが必要です。")
files = sorted(p for p in folder.iterdir() if p.suffix.lower() == ".csv")
if not files or len(files) > 50: raise ValueError("CSVを1〜50ファイル入れてください。")
if any(p.is_symlink() or not p.is_file() for p in files): raise ValueError("通常のCSVファイルだけを入れてください。")
if sum(p.stat().st_size for p in files) > 1024 * 1024: raise ValueError("入力合計は1 MiBまでです。")
blobs = [(p.name, p.read_bytes()) for p in files]
if sum(len(data) for name, data in blobs) > 1024 * 1024: raise ValueError("入力合計は1 MiBまでです。")
csv.field_size_limit(1024 * 1024)
tables = [(name, list(csv.reader(io.StringIO(data.decode("utf-8-sig"), newline=""), strict=True))) for name, data in blobs]
# 2. 見出しと列数を確認。値を勝手に変えない
if any(not table or not table[0] for name, table in tables): raise ValueError("空ファイル・空見出しがあります。")
header = tables[0][1][0]
if any(table[0] != header for name, table in tables): raise ValueError("見出しの名前・順番が一致しません。")
if any(not x.strip() for x in header) or len(set(header)) != len(header): raise ValueError("見出しに空欄・重複があります。")
if "_source_file" in header: raise ValueError("見出しの _source_file は予約名です。")
counts = [(name, sum(bool(row) for row in table[1:])) for name, table in tables]
if not 1 <= sum(count for name, count in counts) <= 10000: raise ValueError("データ合計は1〜10,000件にしてください。")
if any(len(row) != len(header) for name, table in tables for row in table[1:] if row): raise ValueError("データの列数が違います。")
combined = [row + [name] for name, table in tables for row in table[1:] if row]
# 3. 毎回、新しい結果フォルダへ保存
output = Path(tempfile.mkdtemp(prefix="result_", dir=base))
partial = output / "combined.csv.part"
with partial.open("x", encoding="utf-8-sig", newline="") as f: csv.writer(f).writerows([header + ["_source_file"]] + combined)
partial.rename(output / "combined.csv")
report = "\n".join(f"{name}: {count} 件" for name, count in counts)
marker = output / "SUCCESS.txt.part"
with marker.open("x", encoding="utf-8") as f: f.write(report + f"\n合計: {len(combined)} 件\n")
marker.rename(output / "SUCCESS.txt")
print(report + f"\n完了: {len(files)} ファイル → {len(combined)} 件")
print(f"保存先: {output}")
【プログラムここまで】
4 動かして、3件になっているか確認する
保存した merge_csv.py の編集画面で、「Run」→「Run Module」を選びます。F5キーでも実行できます。
成功すると、IDLEのShell画面に次のように表示されます。
01_tokyo.csv: 2 件
02_osaka.csv: 1 件
完了: 2 ファイル → 3 件
保存先: ……/result_……
csv_merge_practice の中に、result_ で始まる新しいフォルダができます。末尾の文字は実行するたびに変わります。
中身は次の2つです。
・combined.csv:結合したデータ
・SUCCESS.txt:ファイルごとの件数と合計。出力が最後まで終わった目印
最初は combined.csv をメモ帳などのテキストエディターで開いてください。次の内容になっていれば、練習は成功です。
商品コード,商品名,数量,_source_file
001,りんご,2,01_tokyo.csv
002,"みかん,小袋",3,01_tokyo.csv
003,ぶどう,1,02_osaka.csv
見出しは1回だけ。商品コードの001も、そのまま残っています。末尾の _source_file が、元のファイル名です。
SUCCESS.txt では、01_tokyo.csv が2件、02_osaka.csv が1件、合計3件になっていることを確認します。件数は見出しを含みません。
5 本番のコピーで試すときも、件数と中身を見る
練習で動いたら、別の練習用フォルダを用意して、本番CSVのコピーで試します。元のファイルは残し、結果が合うか確認してから業務に使いましょう。実行中は input のファイルを編集・追加・削除しないでください。
確認したいのは、次の5つです。
・SUCCESS.txt に、まとめたいファイルがすべて載っているか
・入力ファイルごとの件数が、元のデータ件数と合っているか
・その件数の合計が、画面に表示された出力件数と合っているか
・先頭と末尾のデータ、商品コード、長い番号、日本語が正しいか
・同じ期間のファイルを二重に入れていないか
ファイル名で並べた順に結合します。「1、2、10」の順にしたい場合は、01、02、10のように、入力コピーの名前をそろえると分かりやすくなります。内容を見て日付順に並べ替える機能はありません。
同じ内容の行が2つあっても、そのまま残します。たとえば、同じ商品が2回売れた記録を、誤って消さないためです。重複を取り除きたい場合は、「何が一致したら同じ1件とするか」を先に決める必要があります。
空の行だけは読み飛ばします。ただし「,,」のように空の項目が並んだ行は、データ1件として残します。セルの中に改行があるCSVも扱うため、ここでいう件数は、テキストエディターに表示される物理的な行数とは一致しない場合があります。
6 止まったときの確認場所
エラーが出たら、Shell画面の最後の行を見ます。英語が並んでいても、まずは最後に書かれた名前や説明から確認しましょう。
【見出しの名前・順番が一致しません】
CSVの1行目を見比べます。末尾の空白や、列の順番の違いも対象です。今回は安全のため、表記ゆれを自動修正しません。元の出力設定や、結合してよい組み合わせを確認してください。
【UnicodeDecodeError】
UTF-8として読めないファイルがあります。CP932やShift_JISなど、別の文字コードの可能性があります。このサンプルはUTF-8専用なので、出力元の設定を確認します。文字コードが違うファイルを、そのまま混ぜないでください。エラーが出ずに読めても、日本語の中身は確認します。
【データの列数が違います/_csv.Error】
途中のデータだけ項目が足りない、余計なカンマがある、引用符の付け方が違う、といった原因が考えられます。まずは少量のコピーで、見出しと中身を確認します。カンマを一括削除すると商品名なども変わってしまうので、元のファイルに直接手を入れないようにします。
【容量・ファイル数・件数の上限に関する表示】
今回の教材は、合計1 MiB・50ファイル・10,000件までです。入力をいったんメモリに読み込んで検査するため、小さなCSVに範囲を絞っています。上限の数字を大きくするだけで解決しようとせず、大容量には少しずつ読み込む方式などを検討します。
【PermissionError/OSError】
保存場所へ書き込めない、空き容量が足りない、ファイルを読めない、といった場合があります。エラー文に出ている場所と、パソコンの空き容量・保存先を確認します。会社の権限設定を無理に変えず、必要なら担当者へ相談してください。
【.part のファイルが残っている/SUCCESS.txt がない】
保存の途中で止まっています。そのフォルダの結果は使わないでください。再実行すると、結果は別の新しいフォルダに作られます。
前の実行のSUCCESS.txtが残っていても、今回が成功したことにはなりません。今回の画面に「完了」が出たことと、その直後に表示された保存先を確認しましょう。
7 Excelで開くときだけは、もうひとつ注意
このPython処理では、商品コードや長い番号を数値に変換しません。ただし、できたCSVをExcelで普通に開くと、Excel側で先頭の0が消えたり、長い番号が変わったりすることがあります。
番号を守りたいときは、CSVをダブルクリックせず、Excelの「データ」→「テキストまたはCSVから」などを使い、必要な列を読み込み時点から文字列として扱います。自動で型を変える手順が入っている場合は、その手順を見直します。後からセルの表示形式だけを文字列にしても、既に消えた0は戻りません。
また、このプログラムは、CSVに入っている式のような文字列を無害化する処理ではありません。外部から受け取った信用できないCSVは、表計算ソフトで安易に開かないでください。「=」などで始まる値を数式として扱うソフトもあるためです。まず出所と内容を確認し、必要に応じて担当者へ相談します。
最初の一歩は、小さなCSVを2つまとめるところから
いきなりすべての業務を自動化しなくても、「まとめる」「元ファイルを残す」「件数を確かめる」の3つができると、毎回のコピペを見直しやすくなります。
列が毎回違う、文字コードが混ざる、結合後に集計もしたい。そんな場合は、入力の実物と欲しい結果に合わせて作り方を変えます。
自分の作業ではどこから始めればよいか迷う場合は、ココナラのプロフィールからご相談ください。「今はどうまとめているか」「最後にどんなファイルがほしいか」が分かれば、必要な処理を整理しやすくなります。個人情報や社外秘のファイルは、送る前にご相談ください。
参考にした公式資料など
Python:CSVファイルの読み書き、tempfile、pathlib、IDLE
Microsoft:先頭のゼロと大きい数値を保持する
OWASP:CSV Injection