Python: تحويل JSON إلى CSV — أمثلة DictWriter و pandas
استخدم محوّل JSON إلى CSV المجاني مباشرةً في متصفحك — لا حاجة للتثبيت.
جرّب محوّل JSON إلى CSV أونلاين ←تصطدم كل خطوط بيانات في نهاية المطاف بالخطوة ذاتها: تُعيد واجهة برمجية JSON، لكن المستهلك التالي — جدول بيانات، سكريبت استيراد، أمر Redshift COPY — يحتاج CSV. تحويل JSON إلى CSV في Python يبدو بسيطًا حتى تصطدم بالكائنات المتداخلة أو المفاتيح غير المتسقة أو قيم datetime التي تحتاج معالجة خاصة. يتيح Python مسارَين رئيسيَّين: وحدتا json + csv المدمجتان للسكريبتات بدون تبعيات، و pandas لتسطيح المتداخل والتعامل مع مجموعات البيانات الكبيرة — أو محوّل JSON إلى CSV عبر الإنترنت للتحويلات السريعة الفردية بدون أي كود. يغطي هذا الدليل كلا الأسلوبَين من البداية للنهاية، مع أمثلة Python 3.8+ قابلة للتشغيل.
- ✓csv.DictWriter يحوّل قائمة من القواميس إلى CSV بدون تبعيات — استخدم json.load() للتحليل ثم writeheader() + writerows().
- ✓افتح دائمًا ملفات CSV مع newline="" على Windows لمنع الصفوف الفارغة بين صفوف البيانات.
- ✓pd.json_normalize() تسطّح JSON المتداخل إلى DataFrame مسطّح قبل استدعاء to_csv() — تعالج التداخل متعدد المستويات تلقائيًا.
- ✓مرر index=False إلى DataFrame.to_csv() — بدونه يكتب pandas عمود أرقام صفوف غير مرغوب فيه.
- ✓للملفات فوق 500 ميغابايت، استخدم ijson لبث JSON مع csv.DictWriter لاستخدام ذاكرة ثابتة.
ما هو تحويل JSON إلى CSV؟
يحوّل تحويل JSON إلى CSV مصفوفة من كائنات JSON إلى صيغة جدولية حيث يصبح كل كائن صفًّا وكل مفتاح رأس عمود. JSON هرمي — يمكن أن تتداخل الكائنات بعمق تعسفي. CSV مسطّح — كل قيمة في شبكة صف-عمود. يعمل التحويل بسلاسة عندما تشترك جميع الكائنات في نفس مجموعة المفاتيح على المستوى الأعلى. الكائنات المتداخلة والمصفوفات والمفاتيح غير المتسقة هي ما يُعقّد الأمر. البيانات الخام تبقى متطابقة؛ الهيكل فقط هو الذي يتغير.
order_id,total,status ord_91a3,149.99,shipped ord_b7f2,34.50,pending
[{"order_id":"ord_91a3","total":149.99,"status":"shipped"},
{"order_id":"ord_b7f2","total":34.50,"status":"pending"}]csv.DictWriter — تحويل JSON إلى CSV بدون pandas
وحدة csv تأتي مع كل تثبيت Python. بدون pip install، بدون متاهات البيئة الافتراضية. csv.DictWriter تأخذ قائمة من القواميس وتكتب كل واحد كصف CSV، مربوطةً مفاتيح القاموس بأعمدة الترويسة. يتحكم المعامل fieldnames في ترتيب الأعمدة والمفاتيح المُضمَّنة معًا.
import json
import csv
# بيانات JSON نموذجية — مصفوفة من كائنات الطلبات
json_string = """
[
{"order_id": "ord_91a3", "product": "Wireless Keyboard", "quantity": 2, "unit_price": 74.99},
{"order_id": "ord_b7f2", "product": "USB-C Hub", "quantity": 1, "unit_price": 34.50},
{"order_id": "ord_c4e8", "product": "Monitor Stand", "quantity": 3, "unit_price": 29.95}
]
"""
records = json.loads(json_string)
with open("orders.csv", "w", newline="", encoding="utf-8") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=records[0].keys())
writer.writeheader()
writer.writerows(records)
# orders.csv:
# order_id,product,quantity,unit_price
# ord_91a3,Wireless Keyboard,2,74.99
# ord_b7f2,USB-C Hub,1,34.50
# ord_c4e8,Monitor Stand,3,29.95المعامل newline="" في open() ليس اختياريًا على Windows. بدونه ستحصل على علامات عودة مضاعفة — تظهر كصفوف فارغة بين كل صف بيانات في Excel. على macOS وLinux لا ضرر منه، فقم بتضمينه دائمًا.
الكود أعلاه يستخدم json.loads() لسلسلة نصية. استخدم json.load() (بدون s في النهاية) عند القراءة من مقبض ملف. هذا يُربك الناس باستمرار — واحدة تقرأ سلسلة نصية، والأخرى تقرأ كائن ملف.
import json
import csv
with open("server_metrics.json", encoding="utf-8") as jf:
metrics = json.load(jf) # json.load() لكائنات الملفات
# أسماء الحقول الصريحة تتحكم في ترتيب الأعمدة
columns = ["timestamp", "hostname", "cpu_percent", "memory_mb", "disk_io_ops"]
with open("server_metrics.csv", "w", newline="", encoding="utf-8") as cf:
writer = csv.DictWriter(cf, fieldnames=columns, extrasaction="ignore")
writer.writeheader()
writer.writerows(metrics)
# تظهر الأعمدة الخمسة المحددة فقط، بهذا الترتيب تحديدًاضبط extrasaction="ignore" يتجاهل بصمت أي مفاتيح في القواميس غير موجودة في قائمة fieldnames. القيمة الافتراضية هي "raise"، التي ترفع ValueError إذا كان أي قاموس يحتوي على مفتاح غير متوقع. اختر بناءً على ما تتوقعه من بياناتك.
csv.DictWriter مقابل csv.writer: يربط DictWriter مفاتيح القاموس بمواضع الأعمدة تلقائيًا. csv.writer يكتب قوائم خام كصفوف — أنت تتحكم في ترتيب الأعمدة. DictWriter هو الخيار الأمثل دائمًا تقريبًا لتحويل JSON إلى CSV لأن سجلات JSON هي قواميس بالفعل.وحدة csv في Python تأتي مع ثلاثة أنماط مسماة: excel (محدِّد فاصلة، نهايات CRLF — الافتراضي)، excel-tab (محدِّد تبويب، نهايات CRLF)، و unix (نهايات LF، يقطّر جميع الحقول غير الرقمية). مرر اسم النمط كمعامل dialect إلى csv.DictWriter. يمكنك أيضًا تعريف نمط مخصص بـ csv.register_dialect() عندما يكون للنظام المستهدف قواعد اقتباس أو محدِّدات غير معتادة. في معظم سير عمل JSON إلى CSV يكون نمط excel صحيحًا، لكن تبدّل إلى unix عند كتابة ملفات ستُعالَج بأدوات POSIX مثل awk أو sort.
التعامل مع الأنواع غير القياسية: datetime وUUID وDecimal
JSON من واجهات برمجية غالبًا ما يحتوي على تواريخ كسلاسل ISO، وUUIDs كسلاسل بشرطات، وقيم نقدية كأعداد عشرية. عندما تحللها إلى كائنات Python للمعالجة قبل كتابة CSV، تحتاج إلى تحويلها مجددًا إلى سلاسل نصية. وحدة csv تستدعي str() على كل قيمة، لذا معظم الأنواع تعمل تلقائيًا. لكن كائنات datetime تنتج تمثيلات نصية فوضوية افتراضيًا، وقيم Decimal تحتاج تنسيقًا صريحًا لتجنب الترميز العلمي.
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID
# محاكاة استجابة API محللة بأنواع Python
transactions = [
{
"txn_id": UUID("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
"created_at": datetime(2026, 3, 15, 9, 30, 0, tzinfo=timezone.utc),
"amount": Decimal("1249.99"),
"currency": "USD",
"merchant": "CloudHost Inc.",
},
{
"txn_id": UUID("b2c3d4e5-f6a7-8901-bcde-f12345678901"),
"created_at": datetime(2026, 3, 15, 14, 12, 0, tzinfo=timezone.utc),
"amount": Decimal("87.50"),
"currency": "EUR",
"merchant": "DataSync GmbH",
},
]
def prepare_row(record: dict) -> dict:
"""تحويل الأنواع غير النصية إلى سلاسل مناسبة لـ CSV."""
return {
"txn_id": str(record["txn_id"]),
"created_at": record["created_at"].isoformat(),
"amount": f"{record['amount']:.2f}",
"currency": record["currency"],
"merchant": record["merchant"],
}
with open("transactions.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["txn_id", "created_at", "amount", "currency", "merchant"])
writer.writeheader()
for txn in transactions:
writer.writerow(prepare_row(txn))
# transactions.csv:
# txn_id,created_at,amount,currency,merchant
# a1b2c3d4-e5f6-7890-abcd-ef1234567890,2026-03-15T09:30:00+00:00,1249.99,USD,CloudHost Inc.
# b2c3d4e5-f6a7-8901-bcde-f12345678901,2026-03-15T14:12:00+00:00,87.50,EUR,DataSync GmbHدالة prepare_row() هي المقاربة الصحيحة هنا. بدلًا من محاولة تعليم csv.DictWriter بالأنواع المخصصة، تُطبّع كل سجل إلى سلاسل نصية قبل الكتابة. الأفضل استدعاء .isoformat() صراحةً على كائنات datetime بدلًا من الاعتماد على str() — صيغة المخرجات أكثر قابلية للتنبؤ، والمحللات اللاحقة تتعامل مع ISO 8601 بشكل موثوق.
Decimal بالمرور بدون تنسيق، فقد تُعرض الأعداد الصغيرة جدًا أو الكبيرة جدًا بترميز علمي (مثل 1.5E+7). نسّق دائمًا Decimal بـ f-string صريح مثل f"{value:.2f}" عند كتابة البيانات المالية إلى CSV.نمط بديل لخطوط الأنابيب التي تحتوي على أنواع مخصصة كثيرة هو توسيع json.JSONEncoder. اشتق منه صنفًا فرعيًا، تجاوز دالة default() لإعادة قيمة قابلة للتسلسل JSON لكل نوع مخصص، ثم مرر الصنف الفرعي كمعامل cls إلى json.dumps(). إعادة الترميز عبر المُشفّر المخصص قبل الكتابة إلى CSV يُطبّع جميع الأنواع في خطوة واحدة بدون استدعاء prepare_row() لكل صف. نمط prepare_row() الموضح أعلاه أبسط للسكريبتات الفردية؛ أسلوب الصنف الفرعي JSONEncoder أكثر قابلية للتوسع عندما يُشارَك نفس نموذج النطاق بأنواعه المخصصة عبر مراحل خط أنابيب متعددة أو خدمات مصغرة.
مرجع معاملات csv.DictWriter
التوقيع الكامل للمنشئ هو csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams). معظمها لها قيم افتراضية معقولة. التي ستغيّرها فعلًا هي fieldnames، delimiter، و extrasaction.
pandas — تحويل JSON إلى CSV باستخدام DataFrames
إذا كنت تعمل بالفعل في قاعدة كود تعتمد كثيرًا على pandas، أو كان JSON يحتوي على كائنات متداخلة تحتاج تسطيحها، فأسلوب pandas أقل بكثير في الكود من نسخة stdlib. التنازل: pandas تبعية بحجم ~30 ميغابايت. لسكريبت مؤقت هذا مقبول. لصورة Docker تُشحن للإنتاج، أسلوب stdlib يبقي الأمور أخف.
import pandas as pd
# قراءة مصفوفة JSON مباشرةً إلى DataFrame
df = pd.read_json("warehouse_inventory.json")
# كتابة إلى CSV — index=False يمنع أرقام الصفوف المُولَّدة تلقائيًا
df.to_csv("warehouse_inventory.csv", index=False)
# هذا كل شيء. سطران. pandas يستنتج أنواع الأعمدة تلقائيًا.خيار index=False من تلك الأشياء التي تبحث عنها في كل مرة. بدونه يكتب pandas 0, 1, 2, ... كأول عمود في CSV. لا أحد يريد ذلك.
تسطيح JSON المتداخل بـ json_normalize
استجابات API الحقيقية نادرًا ما تكون مسطّحة. الطلبات تحتوي على عناوين شحن، المستخدمون يحتوون على تفضيلات متداخلة، أحداث التتبع تحتوي على بيانات وصفية متداخلة. pd.json_normalize() تسير على القواميس المتداخلة وتسطّحها إلى أعمدة بأسماء مفصولة بنقطة.
import json
import pandas as pd
api_response = """
[
{
"order_id": "ord_91a3",
"placed_at": "2026-03-15T09:30:00Z",
"customer": {
"name": "Sarah Chen",
"email": "s.chen@example.com",
"tier": "premium"
},
"shipping": {
"method": "express",
"address": {
"city": "Portland",
"state": "OR",
"zip": "97201"
}
},
"total": 299.95
},
{
"order_id": "ord_b7f2",
"placed_at": "2026-03-15T14:12:00Z",
"customer": {
"name": "James Park",
"email": "j.park@example.com",
"tier": "standard"
},
"shipping": {
"method": "standard",
"address": {
"city": "Austin",
"state": "TX",
"zip": "73301"
}
},
"total": 87.50
}
]
"""
orders = json.loads(api_response)
# json_normalize تسطّح القواميس المتداخلة — sep يتحكم في المحدِّد
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)
# الأعمدة الناتجة:
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, totalالمعامل sep="_" يتحكم في كيفية دمج أسماء المفاتيح المتداخلة. الافتراضي هو "."، الذي ينتج أعمدة مثل customer.name. أفضّل الشرطات السفلية لأن النقاط في أسماء الأعمدة تسبب مشكلات في استيرادات SQL وبعض صيغ جداول البيانات.
لاستجابات API التي تُغلّف مصفوفة السجلات تحت مفتاح متداخل، استخدم المعامل record_path. إذا كانت الاستجابة تبدو كـ {"data": {"orders": [...]}}، مرر record_path=["data", "orders"] للتنقل إلى القائمة الصحيحة. المعامل الاختياري meta يتيح لك سحب حقول المستوى الأعلى جنبًا إلى جنب مع السجلات المتداخلة — مفيد عندما تتضمن الاستجابة معلومات ترقيم صفحات على المستوى الأعلى (رقم الصفحة، إجمالي العدد) تريدها كعمود في كل صف. معًا، record_path و meta يتعاملان مع معظم أشكال استجابات API المتداخلة في الواقع بدون معالجة مسبقة مخصصة.
مرجع معاملات DataFrame.to_csv()
DataFrame.to_csv() تحتوي على أكثر من 20 معاملًا. هذه هي المعاملات المهمة لسير عمل JSON إلى CSV.
import pandas as pd
df = pd.read_json("telemetry_events.json")
# مخرجات TSV مع ترميز صريح ومعالجة القيم المفقودة
df.to_csv(
"telemetry_events.tsv",
sep="\t",
index=False,
encoding="utf-8",
na_rep="NULL",
columns=["event_id", "timestamp", "source", "severity", "message"],
)
# الكتابة إلى stdout للتمرير في سكريبتات الشل
print(df.to_csv(index=False))
# إعادة كسلسلة نصية (بدون كتابة ملف)
csv_string = df.to_csv(index=False)
print(len(csv_string), "characters")تحويل JSON إلى CSV من ملف واستجابة API
السيناريوهان الأكثر شيوعًا في الواقع: قراءة JSON من ملف على القرص وتحويله، أو جلب JSON من HTTP API وحفظ النتيجة كـ CSV. في التطوير يمكنك الاستغناء عن معالجة الأخطاء. في الإنتاج، هذا الاختيار يصبح تنبيهًا في الساعة الثانية صباحًا. قد لا تكون الملفات موجودة، قد تُعيد APIs رموز حالة 4xx أو 5xx بدلًا من JSON، قد يكون جسم الاستجابة كائن خطأ وليس مصفوفة، أو قد يكون JSON مبتورًا بسبب انتهاء مهلة الشبكة. الأنماط أدناه تتعامل مع جميع هذه الحالات صراحةً، وتسجّل الأخطاء في stderr، وتُعيد عدد الصفوف حتى تتمكن المُستدعيات من اكتشاف المخرجات الفارغة والتنبيه وفقًا لذلك.
ملف على القرص — قراءة وتحويل وحفظ
import json
import csv
import sys
def json_file_to_csv(input_path: str, output_path: str) -> int:
"""تحويل ملف JSON يحتوي على مصفوفة من الكائنات إلى CSV.
يُعيد عدد الصفوف المكتوبة.
"""
try:
with open(input_path, encoding="utf-8") as jf:
data = json.load(jf)
except FileNotFoundError:
print(f"Error: {input_path} not found", file=sys.stderr)
return 0
except json.JSONDecodeError as exc:
print(f"Error: invalid JSON in {input_path}: {exc.msg} at line {exc.lineno}", file=sys.stderr)
return 0
if not isinstance(data, list) or not data:
print(f"Error: expected a non-empty JSON array in {input_path}", file=sys.stderr)
return 0
# جمع جميع المفاتيح الفريدة عبر جميع السجلات — يتعامل مع المخططات غير المتسقة
all_keys: list[str] = []
seen: set[str] = set()
for record in data:
for key in record:
if key not in seen:
all_keys.append(key)
seen.add(key)
with open(output_path, "w", newline="", encoding="utf-8") as cf:
writer = csv.DictWriter(cf, fieldnames=all_keys, restval="", extrasaction="ignore")
writer.writeheader()
writer.writerows(data)
return len(data)
rows = json_file_to_csv("deploy_logs.json", "deploy_logs.csv")
print(f"Wrote {rows} rows to deploy_logs.csv")استجابة HTTP API — جلب وتحويل
import json
import csv
import urllib.request
import urllib.error
def api_response_to_csv(url: str, output_path: str) -> int:
"""جلب JSON من نقطة نهاية REST API وكتابته كـ CSV."""
try:
req = urllib.request.Request(url, headers={"Accept": "application/json"})
with urllib.request.urlopen(req, timeout=30) as resp:
if resp.status != 200:
print(f"Error: API returned status {resp.status}")
return 0
body = resp.read().decode("utf-8")
except urllib.error.URLError as exc:
print(f"Error: could not reach {url}: {exc.reason}")
return 0
try:
records = json.loads(body)
except json.JSONDecodeError as exc:
print(f"Error: API returned invalid JSON: {exc.msg}")
return 0
if not isinstance(records, list) or not records:
print("Error: expected a non-empty JSON array from the API")
return 0
with open(output_path, "w", newline="", encoding="utf-8") as cf:
writer = csv.DictWriter(cf, fieldnames=records[0].keys())
writer.writeheader()
writer.writerows(records)
return len(records)
rows = api_response_to_csv(
"https://api.internal.example.com/v2/deployments?status=completed",
"completed_deployments.csv",
)
print(f"Exported {rows} deployments to CSV")urllib من المكتبة القياسية للإبقاء على السكريبت بدون تبعيات. إذا كان لديك requests مثبتًا، استبدل قسم urllib بـ resp = requests.get(url, timeout=30); records = resp.json() — بقية كود كتابة CSV يبقى متطابقًا.تحويل JSON إلى CSV من سطر الأوامر
أحيانًا تحتاج فقط سطرًا واحدًا في الطرفية. خيار -c في Python يتيح لك تشغيل تحويل سريع بدون إنشاء ملف سكريبت. للتحويلات الأكثر تعقيدًا، مرر عبر jq أولًا لإعادة تشكيل البيانات، ثم حوّل.
# Python بسطر واحد: يقرأ JSON من stdin ويكتب CSV إلى stdout cat orders.json | python3 -c " import json, csv, sys data = json.load(sys.stdin) w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys()) w.writeheader() w.writerows(data) " # حفظ المخرجات في ملف cat orders.json | python3 -c " import json, csv, sys data = json.load(sys.stdin) w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys()) w.writeheader() w.writerows(data) " > orders.csv
# احفظه كـ json2csv.py وشغّله: python3 json2csv.py input.json -o output.csv
python3 -c "
import json, csv, argparse, sys
parser = argparse.ArgumentParser(description='Convert JSON array to CSV')
parser.add_argument('input', help='Path to JSON file')
parser.add_argument('-o', '--output', default=None, help='Output CSV path (default: stdout)')
parser.add_argument('-d', '--delimiter', default=',', help='CSV delimiter')
args = parser.parse_args()
with open(args.input) as f:
data = json.load(f)
out = open(args.output, 'w', newline='') if args.output else sys.stdout
writer = csv.DictWriter(out, fieldnames=data[0].keys(), delimiter=args.delimiter)
writer.writeheader()
writer.writerows(data)
if args.output:
out.close()
print(f'Wrote {len(data)} rows to {args.output}', file=sys.stderr)
" "$@"# تثبيت csvkit: pip install csvkit
# jq يسطّح ويختار الحقول، in2csv يتعامل مع تنسيق CSV
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv
# Miller (mlr) خيار آخر لتحويل JSON إلى CSV
mlr --json2csv cat orders.json > orders.csvMiller (mlr) ملف ثنائي مستقل يعامل JSON وCSV وTSV كصيغ من الدرجة الأولى بدون حاجة لبيئة Python. خيار --json2csv يحوّل JSON إلى CSV في تمريرة واحدة، ويمكنك ربط أفعال Miller لتصفية أو ترتيب أو إعادة تسمية الأعمدة في نفس الأمر قبل كتابة المخرجات. ثبّته عبر Homebrew على macOS (brew install miller) أو مدير حزم Linux. مفيد بشكل خاص في خطوط أنابيب CI حيث تريد تحويل JSON إلى CSV سريعًا بدون تشغيل بيئة Python.
بديل عالي الأداء — pandas مع pyarrow
لمجموعات البيانات في نطاق عشرات الملايين من الصفوف، pandas مع الخلفية pyarrow تقرأ وتكتب بشكل أسرع بكثير من الافتراضي. محرك Arrow المدعوم بـ C يعالج البيانات العمودية بكفاءة أكبر من وحدة csv الصف بصف في Python. واجهة برمجة التطبيقات تبقى كما هي — تضبط معامل المحرك فقط.
pip install pyarrow
import pandas as pd
# قراءة JSON مع محرك pyarrow (تحليل أسرع للملفات الكبيرة)
df = pd.read_json("sensor_readings.json", engine="pyarrow")
# to_csv لا تمتلك معامل محرك، لكن عمليات DataFrame
# بين القراءة والكتابة تستفيد من التخطيط العمودي لـ pyarrow
df.to_csv("sensor_readings.csv", index=False)
# للتصديرات الكبيرة جدًا، فكّر في الكتابة إلى Parquet بدلًا من CSV
# — صيغة ثنائية، أصغر 5-10x، تحافظ على الأنواع
df.to_parquet("sensor_readings.parquet", engine="pyarrow")إذا كنت تعالج أكثر من بضع مئات من الميغابايتات من JSON والمستهلك النهائي يقبل Parquet، تجاوز CSV كليًا. Parquet أصغر، يحافظ على أنواع الأعمدة، وكل من Redshift وBigQuery يحمّله أصلًا. CSV صيغة مع فقدان بيانات — كل قيمة تصبح سلسلة نصية.
مخرجات الطرفية مع تمييز الصياغة
مكتبة rich تعرض جداول بحدود ومحاذاة وألوان في الطرفية — مفيد لمعاينة التحويل أثناء التطوير بدون فتح ملف المخرجات.
pip install rich
import json
from rich.console import Console
from rich.table import Table
json_string = """
[
{"hostname": "web-prod-1", "cpu_percent": 72.3, "memory_mb": 3840, "uptime_hours": 720},
{"hostname": "web-prod-2", "cpu_percent": 45.1, "memory_mb": 2560, "uptime_hours": 168},
{"hostname": "db-replica-1", "cpu_percent": 91.7, "memory_mb": 7680, "uptime_hours": 2160}
]
"""
records = json.loads(json_string)
console = Console()
table = Table(title="Server Metrics Preview", show_lines=True)
for key in records[0]:
table.add_column(key, style="cyan" if key == "hostname" else "white")
for row in records:
table.add_row(*[str(v) for v in row.values()])
console.print(table)
# يعرض جدولًا ملوّنًا بحدود في الطرفيةcsv.DictWriter أو DataFrame.to_csv()، واستخدم rich للمعاينة فقط.العمل مع ملفات JSON الكبيرة
json.load() يقرأ الملف بالكامل في الذاكرة. لملف JSON بحجم 200 ميغابايت، هذا يعني ~200 ميغابايت من النص الخام بالإضافة إلى حمل كائنات Python — بسهولة 500+ ميغابايت من استخدام الكومة. للملفات فوق 100 ميغابايت، ابثّ المدخلات بـ ijson واكتب صفوف CSV أثناء المعالجة.
pip install ijson
بث مصفوفة JSON إلى CSV باستخدام ijson
import ijson
import csv
def stream_json_to_csv(json_path: str, csv_path: str) -> int:
"""تحويل مصفوفة JSON كبيرة إلى CSV بدون تحميلها كلها في الذاكرة."""
with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
# ijson.items يُنتج كل عنصر من المصفوفة الأعلى مستوى واحدًا في كل مرة
records = ijson.items(jf, "item")
first_record = next(records)
fieldnames = list(first_record.keys())
writer = csv.DictWriter(cf, fieldnames=fieldnames)
writer.writeheader()
writer.writerow(first_record)
count = 1
for record in records:
writer.writerow(record)
count += 1
return count
rows = stream_json_to_csv("clickstream_2026_03.json", "clickstream_2026_03.csv")
print(f"Streamed {rows} records to CSV")NDJSON / JSON Lines — كائن واحد في كل سطر
NDJSON (JSON المحدَّد بسطر جديد)، المعروف أيضًا بـ JSON Lines أو .jsonl، يخزّن كائن JSON صالح واحد في كل سطر بدون مصفوفة شاملة. هذه الصيغة شائعة في خطوط أنابيب السجلات وتدفقات الأحداث (Kafka، Kinesis)، والتصديرات الجماعية من خدمات مثل Elasticsearch وBigQuery. لأن كل سطر كائن JSON مكتفٍ بذاته، يمكنك معالجة ملف NDJSON بحلقة Python العادية على مقبض الملف — لا حاجة لمكتبة ijson. تبقى الذاكرة ثابتة بصرف النظر عن حجم الملف، مما يجعل هذا أبسط أسلوب بث عندما تكون بيانات المصدر بالفعل بصيغة JSON Lines.
import json
import csv
def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
"""تحويل ملف JSON محدَّد بسطر جديد إلى CSV، سطرًا في كل مرة."""
with open(ndjson_path, encoding="utf-8") as nf:
first_line = nf.readline()
first_record = json.loads(first_line)
fieldnames = list(first_record.keys())
with open(csv_path, "w", newline="", encoding="utf-8") as cf:
writer = csv.DictWriter(cf, fieldnames=fieldnames)
writer.writeheader()
writer.writerow(first_record)
count = 1
for line in nf:
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
writer.writerow(record)
count += 1
except json.JSONDecodeError:
continue # تجاوز الأسطر المشوهة
return count
rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"Converted {rows} log entries to CSV")json.load() يمكن أن تستهلك 3–5 غيغابايت من RAM بسبب حمل كائنات Python. مع ijson تبقى الذاكرة ثابتة بصرف النظر عن حجم الملف. إذا كنت تحتاج فقط تحويلًا سريعًا لملف صغير، الصقه في محوّل JSON إلى CSV بدلًا من ذلك.الأخطاء الشائعة
المشكلة: وحدة csv تكتب نهايات أسطر . بدون newline=''، يضيف وضع النص في Python آخر على Windows، مما ينتج مخرجات بتباعد مضاعف.
الحل: مرر دائمًا newline='' عند فتح ملف لكتابة CSV. لا ضرر منه على macOS/Linux.
with open("output.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# مخرجات نظيفة على جميع المنصاتwith open("output.csv", "w") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# صفوف فارغة بين كل صف بيانات على Windowsالمشكلة: بدون index=False، تُضيف pandas عمود أرقام صفوف متصاعدًا تلقائيًا (0، 1، 2، ...) يلوّث CSV ببيانات لم تكن في JSON الأصلي.
الحل: مرر index=False إلى to_csv(). إذا كنت تحتاج فعلًا عمود فهرس، سمّه صراحةً بـ df.index.name = 'row_num'.
df = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# CSV نظيف: event_id,timestamp,...df = pd.read_json("events.json")
df.to_csv("events.csv")
# CSV يحصل على عمود إضافي غير مسمى: ,event_id,timestamp,...
# الفاصلة الأولى تُعطّل كثيرًا من محللي CSVالمشكلة: إذا كانت كائنات JSON بمفاتيح مختلفة (بعض السجلات بحقول اختيارية)، فاستخدام مفاتيح أول سجل كأسماء حقول يتجاهل بصمت الأعمدة التي تظهر فقط في السجلات اللاحقة.
الحل: اجمع جميع المفاتيح الفريدة عبر جميع السجلات قبل إنشاء DictWriter.
records = json.load(f) all_keys = list(dict.fromkeys(k for r in records for k in r)) writer = csv.DictWriter(out, fieldnames=all_keys, restval="") # كل مفتاح من كل سجل مُضمَّن كعمود
records = json.load(f) writer = csv.DictWriter(out, fieldnames=records[0].keys()) # يُفوّت حقل "discount" الذي يظهر فقط في records[2]
المشكلة: csv.DictWriter يستدعي str() على القواميس المتداخلة، منتجًا أعمدة بقيم مثل "{'city': 'Portland'}"— تمثيل Python الخام، وليس بيانات فعلية.
الحل: سطّح الكائنات المتداخلة أولًا باستخدام pd.json_normalize() أو دالة تسطيح مخصصة.
import pandas as pd
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
df = pd.json_normalize(records, sep="_")
df.to_csv("events.csv", index=False)
# الأعمدة: id, meta_source, meta_regionrecords = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
writer = csv.DictWriter(f, fieldnames=["id", "meta"])
writer.writerows(records)
# عمود meta يحتوي: {'source': 'web', 'region': 'us-west'}csv.DictWriter مقابل pandas — مقارنة سريعة
استخدم csv.DictWriter عندما تحتاج صفر تبعيات، وJSON مسطّح، والسكريبت يعمل في بيئة مقيّدة (حاويات CI، دوال Lambda، Python مضمّن). استخدم pd.json_normalize() + to_csv() عندما يكون JSON متداخلًا، أو تحتاج تحويل أو تصفية البيانات قبل التصدير، أو أنت بالفعل في سير عمل pandas. للملفات التي لا تناسب الذاكرة، ادمج ijson مع csv.DictWriter للبث بذاكرة ثابتة.
للتحويلات السريعة بدون كود، محوّل JSON إلى CSV على ToolDeck يتولى الأمر بدون أي إعداد Python.
الأسئلة الشائعة
كيف أحوّل JSON إلى CSV في Python بدون pandas؟
استخدم وحدتَي json وcsv المدمجتين. استدعِ json.load() لتحليل ملف JSON إلى قائمة من القواميس، واستخرج أسماء الحقول من مفاتيح أول قاموس، أنشئ csv.DictWriter، ثم استدعِ writeheader() ثم writerows(). لا يتطلب هذا الأسلوب أي تبعيات خارجية ويعمل في أي بيئة Python 3.x. كما يعمل بشكل أسرع من pandas للملفات الصغيرة إذ لا يوجد تحميل إضافي لإنشاء DataFrame. إذا كانت كائنات JSON تحتوي على مفاتيح غير متسقة بين السجلات، اجمع جميع المفاتيح الفريدة أولًا باستخدام dict.fromkeys(k for r in records for k in r) قبل تمريرها كـ fieldnames لتجنب الأعمدة المفقودة.
import json
import csv
with open("orders.json") as f:
records = json.load(f)
with open("orders.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=records[0].keys())
writer.writeheader()
writer.writerows(records)كيف أتعامل مع JSON المتداخل عند التحويل إلى CSV؟
تتحول مصفوفات JSON المسطّحة مباشرةً إلى صفوف CSV، أما الكائنات المتداخلة فتحتاج إلى تسطيح أولًا. مع pandas، تتولى pd.json_normalize() هذا تلقائيًا — تدمج المفاتيح المتداخلة بمحدِّد نقطة (مثلًا "address.city"). بدون pandas، اكتب دالة تعاودية تمشي على القاموس وتسلسل المفاتيح بمحدِّد. للهياكل المتداخلة بعمق بمستويات متعددة، تعالجها json_normalize دفعةً واحدة. يتحكم المعامل sep في محرف الدمج بين شرائح المفاتيح — عادةً الشرطة السفلية أأمن من النقطة الافتراضية للاستيرادات SQL وتوافق صيغ جداول البيانات.
import pandas as pd
nested_data = [
{"id": "ord_91a3", "customer": {"name": "فاطمة أحمد", "email": "f.ahmed@example.com"}},
]
df = pd.json_normalize(nested_data, sep="_")
# الأعمدة: id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)لماذا تظهر صفوف فارغة بين صفوف البيانات في CSV على Windows؟
تكتب وحدة csv نهايات أسطر \r\n افتراضيًا. على Windows، يضيف فتح الملف في وضع النص \r آخر، مما ينتج \r\r\n — يظهر كصف فارغ. الحل هو تمرير newline="" دائمًا إلى open(). هذا يخبر Python بعدم ترجمة نهايات الأسطر، تاركًا وحدة csv تتحكم فيها. هذا النمط مطلوب بصرف النظر عن نظام التشغيل — لا ضرر منه على macOS وLinux، وضروري على Windows. توثيق Python يستشهد بهذا صراحةً في قسم وحدة csv باعتباره الطريقة الصحيحة لفتح الملفات للكتابة بتنسيق CSV.
# خطأ — صفوف فارغة على Windows
with open("output.csv", "w") as f:
writer = csv.writer(f)
# صحيح — newline="" يمنع \r المضاعف
with open("output.csv", "w", newline="") as f:
writer = csv.writer(f)كيف أُلحق سجلات JSON بملف CSV موجود؟
افتح الملف في وضع الإلحاق ("a") وأنشئ DictWriter بنفس أسماء الحقول. تجاوز writeheader() لأن صف الترويسة موجود بالفعل. مع pandas، استخدم to_csv(mode="a", header=False). تأكد من أن ترتيب الأعمدة يطابق الملف الموجود وإلا ستهبط البيانات في الأعمدة الخطأ. إن لم تكن متأكدًا من ترتيب الأعمدة في الملف الموجود، افتحه أولًا بـ csv.DictReader واقرأ أسماء الحقول من خاصية fieldnames قبل إنشاء الكاتب للإلحاق.
import csv
new_records = [
{"order_id": "ord_f4c1", "total": 89.50, "status": "shipped"},
]
with open("orders.csv", "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["order_id", "total", "status"])
writer.writerows(new_records)ما أسرع طريقة لتحويل ملف JSON كبير إلى CSV في Python؟
للملفات دون 500 ميغابايت، pd.read_json() متبوعةً بـ to_csv() هي الأسرع في استدعاء واحد — تستخدم pandas كود C محسَّنًا داخليًا. للملفات فوق 500 ميغابايت، استخدم ijson لبث سجلات JSON واكتبها إلى CSV بـ csv.DictWriter صفًّا بصف. هذا يبقي استخدام الذاكرة ثابتًا بصرف النظر عن حجم الملف. لملفات NDJSON (كائن JSON واحد في كل سطر)، لا تحتاج ijson إطلاقًا — حلقة Python العادية على مقبض الملف تعالج كل سطر باستقلالية وتحقق ذاكرة ثابتة بدون أي مكتبة خارجية.
# سريع للملفات التي تناسب الذاكرة
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)
# بث للملفات التي لا تناسب الذاكرة
import ijson, csv
with open("huge.json", "rb") as jf, open("huge.csv", "w", newline="") as cf:
records = ijson.items(jf, "item")
first = next(records)
writer = csv.DictWriter(cf, fieldnames=first.keys())
writer.writeheader()
writer.writerow(first)
for record in records:
writer.writerow(record)هل يمكنني كتابة مخرجات CSV إلى stdout بدلًا من ملف في Python؟
نعم. مرر sys.stdout كائن الملف إلى csv.writer() أو csv.DictWriter(). هذا مفيد لتمرير المخرجات في سكريبتات الشل أو التصحيح السريع. مع pandas، استدعِ to_csv(sys.stdout, index=False) أو to_csv(None) للحصول على سلسلة نصية يمكن طباعتها. لا حاجة لملف مؤقت. عند الكتابة إلى stdout على Windows، استدعِ sys.stdout.reconfigure(newline="") أولًا لتجنب مشكلة علامة العودة المضاعفة، لأن stdout يفتح في وضع النص افتراضيًا.
import csv
import sys
import json
data = json.loads('[{"host":"web-1","cpu":72.3},{"host":"web-2","cpu":45.1}]')
writer = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
# host,cpu
# web-1,72.3
# web-2,45.1أدوات ذات صلة
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.
Priya is a data scientist and machine learning engineer who has worked across the full Python data stack — from raw data ingestion and cleaning to model deployment and monitoring. She is passionate about reproducible research, Jupyter-based workflows, and the practical engineering side of ML. She writes about NumPy, Pandas, data serialisation, and the Python patterns that make data pipelines reliable at scale.