Python: تحويل JSON إلى CSV — أمثلة DictWriter و pandas

·Backend Developer·مراجعة بواسطةPriya Sharma·نُشر

استخدم محوّل 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 مسطّح — كل قيمة في شبكة صف-عمود. يعمل التحويل بسلاسة عندما تشترك جميع الكائنات في نفس مجموعة المفاتيح على المستوى الأعلى. الكائنات المتداخلة والمصفوفات والمفاتيح غير المتسقة هي ما يُعقّد الأمر. البيانات الخام تبقى متطابقة؛ الهيكل فقط هو الذي يتغير.

After · json
Before · json
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 في ترتيب الأعمدة والمفاتيح المُضمَّنة معًا.

Python 3.8+ — مثال json to csv بسيط
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 في النهاية) عند القراءة من مقبض ملف. هذا يُربك الناس باستمرار — واحدة تقرأ سلسلة نصية، والأخرى تقرأ كائن ملف.

Python 3.8+ — قراءة ملف JSON وكتابة ملف CSV
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 تحتاج تنسيقًا صريحًا لتجنب الترميز العلمي.

Python 3.8+ — معالجة datetime وDecimal مسبقًا قبل كتابة CSV
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.

المعامل
النوع
الافتراضي
الوصف
f
file object
(مطلوب)
أي كائن يحتوي على دالة write()‎ — عادةً من open()‎
fieldnames
sequence
(مطلوب)
قائمة مفاتيح تحدد ترتيب الأعمدة في مخرجات CSV
restval
str
""
القيمة المكتوبة عندما يفتقر القاموس إلى مفتاح من fieldnames
extrasaction
str
"raise"
"raise" يرفع ValueError للمفاتيح الإضافية؛ "ignore" يتجاهلها بصمت
dialect
str / Dialect
"excel"
قواعد تنسيق مسبقة — "excel"، أو "excel-tab"، أو "unix"
delimiter
str
","
محرف واحد يفصل الحقول — استخدم "\t" لمخرجات TSV
quotechar
str
"
المحرف المستخدم لتقطير الحقول التي تحتوي على المحدِّد
quoting
int
csv.QUOTE_MINIMAL
يتحكم في متى يُطبَّق التقطير — MINIMAL أو ALL أو NONNUMERIC أو NONE
lineterminator
str
"\r\n"
السلسلة المُلحَقة بعد كل صف — بدّلها إلى "\n" لمخرجات Unix

pandas — تحويل JSON إلى CSV باستخدام DataFrames

إذا كنت تعمل بالفعل في قاعدة كود تعتمد كثيرًا على pandas، أو كان JSON يحتوي على كائنات متداخلة تحتاج تسطيحها، فأسلوب pandas أقل بكثير في الكود من نسخة stdlib. التنازل: pandas تبعية بحجم ~30 ميغابايت. لسكريبت مؤقت هذا مقبول. لصورة Docker تُشحن للإنتاج، أسلوب stdlib يبقي الأمور أخف.

Python 3.8+ — pandas read_json ثم to_csv
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() تسير على القواميس المتداخلة وتسطّحها إلى أعمدة بأسماء مفصولة بنقطة.

Python 3.8+ — تسطيح JSON المتداخل باستخدام 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.

المعامل
النوع
الافتراضي
الوصف
path_or_buf
str / Path / None
None
مسار الملف أو المخزن المؤقت — None يُعيد CSV كسلسلة نصية
sep
str
","
محدِّد الحقول — استخدم "\t" لـ TSV
index
bool
True
كتابة فهرس الصف كأول عمود — في الغالب اضبطه على False
columns
list
None
تحديد الأعمدة وإعادة ترتيبها في المخرجات
header
bool / list
True
كتابة أسماء الأعمدة — اضبطه False عند الإلحاق بملف موجود
encoding
str
"utf-8"
ترميز المخرجات — استخدم "utf-8-sig" للتوافق مع Excel على Windows
na_rep
str
""
تمثيل نصي للقيم المفقودة (NaN، None)
quoting
int
csv.QUOTE_MINIMAL
يتحكم في متى تُقطَّر الحقول
Python 3.8+ — to_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، وتُعيد عدد الصفوف حتى تتمكن المُستدعيات من اكتشاف المخرجات الفارغة والتنبيه وفقًا لذلك.

ملف على القرص — قراءة وتحويل وحفظ

Python 3.8+ — تحويل ملف JSON إلى CSV مع معالجة الأخطاء
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 — جلب وتحويل

Python 3.8+ — جلب JSON من API وحفظه كـ CSV
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 أولًا لإعادة تشكيل البيانات، ثم حوّل.

bash — تحويل json إلى csv بسطر واحد
# 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
bash — سكريبت CLI مكتفٍ بذاته مع argparse
# احفظه كـ 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)
" "$@"
bash — استخدام jq + csvkit للتحويلات المعقدة
# تثبيت 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.csv

Miller (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. واجهة برمجة التطبيقات تبقى كما هي — تضبط معامل المحرك فقط.

bash — تثبيت pyarrow
pip install pyarrow
Python 3.8+ — pandas مع pyarrow لكتابة CSV أسرع
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 تعرض جداول بحدود ومحاذاة وألوان في الطرفية — مفيد لمعاينة التحويل أثناء التطوير بدون فتح ملف المخرجات.

bash — تثبيت rich
pip install rich
Python 3.8+ — معاينة مخرجات CSV في الطرفية باستخدام 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)
# يعرض جدولًا ملوّنًا بحدود في الطرفية
تحذير:rich للعرض في الطرفية فقط. لا تستخدمها لتوليد ملفات CSV — تضيف رموز ANSI Escape التي ستُفسد المخرجات. اكتب إلى الملفات بـ csv.DictWriter أو DataFrame.to_csv()، واستخدم rich للمعاينة فقط.

العمل مع ملفات JSON الكبيرة

json.load() يقرأ الملف بالكامل في الذاكرة. لملف JSON بحجم 200 ميغابايت، هذا يعني ~200 ميغابايت من النص الخام بالإضافة إلى حمل كائنات Python — بسهولة 500+ ميغابايت من استخدام الكومة. للملفات فوق 100 ميغابايت، ابثّ المدخلات بـ ijson واكتب صفوف CSV أثناء المعالجة.

bash — تثبيت ijson
pip install ijson

بث مصفوفة JSON إلى CSV باستخدام ijson

Python 3.8+ — بث مصفوفة JSON كبيرة إلى CSV بذاكرة ثابتة
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.

Python 3.8+ — تحويل NDJSON إلى CSV سطرًا بسطر
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 100 ميغابايت. مصفوفة JSON بحجم 1 غيغابايت محمّلة بـ json.load() يمكن أن تستهلك 3–5 غيغابايت من RAM بسبب حمل كائنات Python. مع ijson تبقى الذاكرة ثابتة بصرف النظر عن حجم الملف. إذا كنت تحتاج فقط تحويلًا سريعًا لملف صغير، الصقه في محوّل JSON إلى CSV بدلًا من ذلك.

الأخطاء الشائعة

غياب newline='' في open()‎ — صفوف فارغة على Windows

المشكلة: وحدة csv تكتب نهايات أسطر . بدون newline=''، يضيف وضع النص في Python آخر على Windows، مما ينتج مخرجات بتباعد مضاعف.

الحل: مرر دائمًا newline='' عند فتح ملف لكتابة CSV. لا ضرر منه على macOS/Linux.

After · Python
Before · Python
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 to_csv()‎

المشكلة: بدون index=False، تُضيف pandas عمود أرقام صفوف متصاعدًا تلقائيًا (0، 1، 2، ...) يلوّث CSV ببيانات لم تكن في JSON الأصلي.

الحل: مرر index=False إلى to_csv()‎. إذا كنت تحتاج فعلًا عمود فهرس، سمّه صراحةً بـ df.index.name = 'row_num'.

After · Python
Before · Python
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
استخدام records[0].keys()‎ عندما السجلات بمفاتيح غير متسقة

المشكلة: إذا كانت كائنات JSON بمفاتيح مختلفة (بعض السجلات بحقول اختيارية)، فاستخدام مفاتيح أول سجل كأسماء حقول يتجاهل بصمت الأعمدة التي تظهر فقط في السجلات اللاحقة.

الحل: اجمع جميع المفاتيح الفريدة عبر جميع السجلات قبل إنشاء DictWriter.

After · Python
Before · Python
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 بدون تسطيح

المشكلة: csv.DictWriter يستدعي str()‎ على القواميس المتداخلة، منتجًا أعمدة بقيم مثل "{'city': 'Portland'}"— تمثيل Python الخام، وليس بيانات فعلية.

الحل: سطّح الكائنات المتداخلة أولًا باستخدام pd.json_normalize()‎ أو دالة تسطيح مخصصة.

After · Python
Before · Python
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_region
records = [{"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 — مقارنة سريعة

الطريقة
JSON متداخل
أنواع مخصصة
البث
التبعيات
يتطلب تثبيتًا
csv.DictWriter
✗ (تسطيح يدوي)
✓ (صف بصف)
لا شيء
لا (stdlib)
csv.writer
✓ (صف بصف)
لا شيء
لا (stdlib)
pd.DataFrame.to_csv()
✗ (مسطّح فقط)
✓ (عبر dtypes)
pandas + numpy
pip install
pd.json_normalize() + to_csv()
✓ (عبر dtypes)
pandas + numpy
pip install
csv.writer + json_flatten
flatten_json
pip install
jq + csvkit (CLI)
✓ (عبر jq)
N/A
jq, csvkit
تثبيت على مستوى النظام

استخدم 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 لتجنب الأعمدة المفقودة.

Python
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 وتوافق صيغ جداول البيانات.

Python
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.

Python
# خطأ — صفوف فارغة على 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 قبل إنشاء الكاتب للإلحاق.

Python
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 العادية على مقبض الملف تعالج كل سطر باستقلالية وتحقق ذاكرة ثابتة بدون أي مكتبة خارجية.

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 يفتح في وضع النص افتراضيًا.

Python
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

أدوات ذات صلة

MS
Maria SantosBackend Developer

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.

PS
Priya Sharmaالمراجع التقني

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.