Skip to content

Beginner: data foundations · Python for data · topic 3 of 62 · 30 min

Reading CSV and JSON with the standard library

Explanation

Most data arrives as CSV (rows of comma-separated text) or JSON (labelled, nested data from APIs). Python's csv module turns CSV text into lists or dicts, and json.loads turns JSON text into dicts and lists. Every CSV value arrives as text, so you must convert numbers yourself and decide what to do with blanks.

Why these two formats

CSV means comma-separated values. It is a plain text file where each line is a row and commas separate the columns; the first line usually holds the column names (the header). Excel, Google Sheets, bank statements and government data portals can all export CSV. JSON (JavaScript Object Notation) is the format most web APIs use: it has objects in curly brackets and lists in square brackets, which map straight onto Python dicts and lists.

Text that behaves like a file

The csv module reads from a file. In the browser tasks there are no files, so we keep the CSV in a string and wrap it with io.StringIO, which makes a string behave like an open file. At work you would use open() on a real file instead; everything else stays the same.

csv.reader: rows as lists

python
import csv, io

text = """item,qty,price
milk,2,30
bread,1,45
rice,5,60
"""
for row in csv.reader(io.StringIO(text)):
    print(row)
# ['item', 'qty', 'price']
# ['milk', '2', '30']
# ['bread', '1', '45']
# ['rice', '5', '60']
Each row is a list of strings. Notice that 2 and 30 are text, in quotes.

csv.DictReader: rows as dicts

DictReader reads the header line for you and gives each row as a dict keyed by column name. That is exactly the list-of-dicts table from the previous topic, so it is usually the better choice.

python
rows = list(csv.DictReader(io.StringIO(text)))
print(rows[0])   # {'item': 'milk', 'qty': '2', 'price': '30'}

total = sum(int(r["qty"]) * int(r["price"]) for r in rows)
print(total)     # 405
int() turns "2" into 2. Without it, "2" * "30" is an error. 2×30 + 1×45 + 5×60 = 405.

Blanks and messy values

Real files have gaps. A blank cell arrives as an empty string "", and int("") raises a ValueError. Decide on purpose what a blank means: skip the row, count it as 0, or keep it as None (missing). For money and marks, None is usually the honest choice, because 0 is a real value. Module 4 goes deeper into handling missing data.

python
text = """name,city,salary
Asha,Pune,52000
Ravi,"Mumbai, MH",
Meena,Delhi,61000
"""
for r in csv.DictReader(io.StringIO(text)):
    salary = int(r["salary"]) if r["salary"].strip() else None
    print(r["name"], r["city"], salary)
# Asha Pune 52000
# Ravi Mumbai, MH None
# Meena Delhi 61000
The csv module handles the quoted "Mumbai, MH" correctly. line.split(",") would break it into two columns.

JSON: loads and dumps

json.loads (load string) turns JSON text into Python values: objects become dicts, arrays become lists, true and false become True and False, and null becomes None. json.dumps (dump string) goes the other way, which is how you send data to an API or save it.

python
import json

text = '{"city": "Pune", "orders": [250, 120], "paid": true, "coupon": null}'
data = json.loads(text)
print(data["orders"], sum(data["orders"]))   # [250, 120] 370
print(data["paid"], data["coupon"])           # True None

print(json.dumps({"item": "chai", "price": 15}))           # {"item": "chai", "price": 15}
print(json.dumps({"price": "₹250"}))                      # {"price": "\u20b9250"}
print(json.dumps({"price": "₹250"}, ensure_ascii=False))  # {"price": "₹250"}
By default dumps escapes non-English characters like ₹; ensure_ascii=False keeps them readable. indent=2 makes the output pretty.

At work: real files and pandas

python
import csv

with open("sales.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))

import pandas as pd
df = pd.read_csv("sales.csv")   # pandas also guesses number types for you
Shown only. newline="" is what the csv docs recommend; encoding="utf-8" avoids garbled Hindi or ₹ text.

Common mistakes

  • Doing maths on text: "30" + "45" gives "3045". Fix: convert with int() or float() as soon as you read the value.
  • Splitting lines with split(","). It breaks on quoted values like "Mumbai, MH". Fix: always use the csv module.
  • Crashing on blanks with int(""). Fix: check value.strip() first and use None (or skip the row) on purpose.
  • Mixing up loads and load. Fix: loads and dumps work with strings; load and dump work with open files.
  • Writing JSON by hand with single quotes. JSON needs double quotes, so {'a': 1} is not valid JSON. Fix: build a dict and call json.dumps.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Reading CSV and JSON with the standard library · 1/7

CSV and JSON

In this lesson you will read the two most common data formats, CSV and JSON, using only Python's standard library.

Prefer a video by a teacher? Find free videos on “python csv DictReader json loads tutorial” on YouTube ↗

In real life: Rainfall data from a government portal

India's Open Government Data Platform (data.gov.in) publishes many datasets, such as rainfall by district, as CSV files you can download, and some through an API that returns JSON. A typical first step is to read the CSV, convert the rainfall column to numbers, skip blank months, and total by district. The values below are made-up sample numbers, not real rainfall.

python
import csv, io

text = """district,month,rain_mm
Pune,Jun,180.5
Pune,Jul,
Nashik,Jun,120.0
Nashik,Jul,210.25
"""
total = 0.0
for r in csv.DictReader(io.StringIO(text)):
    if r["rain_mm"].strip():
        total += float(r["rain_mm"])
print(round(total, 2))
The blank July value for Pune is skipped. Output: 510.75.

Practice (0/3 done)

Task 1 · Code · Python

Write parse_items(text) that reads CSV text with the header item,qty,price and returns a list of dicts in which "qty" and "price" are ints. Example row: {"item": "milk", "qty": 2, "price": 30}. Use csv.DictReader with io.StringIO.

Task 2 · Code · Python

Write column_total(text, column) that reads CSV text (with a header) and returns the total of the named column as a float rounded to 2 decimal places. Skip blank cells (empty or only spaces). If there are no numbers, return 0.0.

Task 3 · Code · Python

An API returns JSON text like {"orders": [{"id": 1, "amount": 250, "status": "paid"}, ...]}. Write total_paid(json_text) that returns the total amount of orders whose status is "paid". If there are no paid orders, return 0.

Quick quiz

Question 1

1.A CSV row is read with csv.DictReader. What type is r["qty"] for the value 5?

Question 2

2.Why not read CSV lines with line.split(",")?

Question 3

3.What does json.loads('{"ok": true, "note": null}') give?

Question 4

4.What does io.StringIO(text) do?

Question 5

5.A salary cell is blank. Which choice is usually most honest when you store it?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A CSV row is read with csv.DictReader. What type is r["qty"] for the value 5?

Answer: str

The csv module never converts types. Every value is a string until you convert it.

Why not read CSV lines with line.split(",")?

Answer: Quoted values that contain commas, like "Mumbai, MH", get broken into two columns

The csv module understands quotes; a plain split does not.

What does json.loads('{"ok": true, "note": null}') give?

Answer: {'ok': True, 'note': None}

JSON true becomes Python True and null becomes None.

What does io.StringIO(text) do?

Answer: Makes a string behave like an open file

csv readers expect a file; StringIO wraps a string so it can be read like one.

A salary cell is blank. Which choice is usually most honest when you store it?

Answer: None, meaning missing

0 is a real salary and would drag the average down. None marks the value as missing so you can decide later, openly, how to handle it.

What does csv.DictReader give you for each row?Related

Answer: A dict with the header names as keys

DictReader uses the header row as keys, so you can write row["marks"].

From CSV files and simple statistics

You just loaded a new CSV. Which command shows each column's type and how many values are missing?Related

Answer: df.info()

info() lists dtypes and non-null counts.

From pandas basics: Series and DataFrames

What does json.dumps(True) return?Related

Answer: "true"

JSON writes booleans in small letters, so you get the text true.

From JSON and regular expressions

A date column has "05/01/2025" in Indian style. What should you do?Related

Answer: Pass format="%d/%m/%Y" explicitly

Guessing may read it as May 1. An explicit format avoids the mix-up.

From Cleaning messy data

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Python for data” before its exam: Counting and grouping: collections and statistics, Writing data code that others can trust.
  2. Module 1 exam: Python for data · 14 questions · 20 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Statistics made simple. 62 topics left in the course before the final exam.

← Previous: Python essentials for data: records, lists and dictionaries