Some tools (older import forms, email services, spreadsheet apps) choke on big CSV files. A common fix is to split the file into smaller parts, with the header row repeated in every part. Here is a small script that does exactly that using only Python's standard library.
Why not just split -l?
The Unix split command cuts by line count. That breaks in two ways:
- The header only ends up in the first file.
- A quoted field that contains a line break can be cut in half.
Python's csv module reads whole records, so multi-line fields stay intact.
The script
import csv import sys from pathlib import Path def split_csv(src, rows_per_file, out_dir="."): src = Path(src) out_dir = Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) written = [] with src.open(newline="", encoding="utf-8-sig") as f: reader = csv.reader(f) header = next(reader) writer = None out = None count = 0 part = 0 for row in reader: if writer is None or count >= rows_per_file: if out: out.close() part += 1 path = out_dir / f"{src.stem}_part{part:03d}.csv" out = path.open("w", newline="", encoding="utf-8") writer = csv.writer(out) writer.writerow(header) written.append(path) count = 0 writer.writerow(row) count += 1 if out: out.close() return written if __name__ == "__main__": if len(sys.argv) < 3: sys.exit("Usage: python split_csv.py input.csv rows_per_file [out_dir]") for p in split_csv(sys.argv[1], int(sys.argv[2]), *(sys.argv[3:4])): print(p) Usage
python split_csv.py products.csv 1000 parts/ This writes products_part001.csv, products_part002.csv and so on into parts/, each with at most 1000 data rows plus the header.
Quick test
I tested it with a 25-row file that included fields containing commas and line breaks, split at 10 rows. Result: three files with 10, 10 and 5 data rows, every file starting with the same header, and 25 rows in total, so nothing was lost or duplicated.
Notes
-
utf-8-sigon input strips a BOM if Excel added one; the output is plain UTF-8. - The file is streamed row by row, so memory use stays small even for large files.
- If your target tool needs a BOM (some Excel versions do), change the output encoding to
utf-8-sig.
If you only need the rows count per part, adjust the second argument. That is all there is to it.


