API 回來的資料不一定完整,可能會有缺失值、重複值,甚至格式不一致。
因此今天我要來學習基礎的資料清理方法,並完成一個小實作,把 API 的資料處理乾淨後再存檔。
1. 缺失值問題
有時候 API 回傳的資料不完整,可能漏掉欄位,或只給空字串、null。
如果是關鍵欄位沒有資料,那這筆資料可能沒辦法用。
2. 重複值問題
API 來的資料可能會重複,特別是多個來源合併的時候。
如果不去除重複,計算結果或查詢會不準確。
3. 資料一致性問題
比如 Email 地址大小寫不統一,文字欄位多了空白,這會影響後續分析。
清理後,資料格式比較一致,也比較好用。
1. 缺失值處理(Missing Values)
有些資料欄位沒填資料,叫做缺失值。
刪除法:如果關鍵欄位沒資料,可以把這整筆資料刪掉。
填補法:如果是非必要欄位沒資料,可以用空字串、None(空值),或是預設的數值替代。
2. 重複值處理(Duplicates)
同一筆資料有可能出現多次,特別是從不同來源整合時。
3. 資料標準化(Normalization)
把資料格式統一,讓分析時不會出錯。
常見的作法有:
用 .strip()
方法去除文字前後多餘空白。
把 Email 都轉成小寫,避免一樣的 Email 被當作不同。
統一欄位名稱,避免有時是「Email」,有時是「e-mail」。
步驟 1:抓取 API 資料
用 JSONPlaceholder 這個免費的測試 API 抓「users」資料。
把原始資料存成 json 檔(users_raw.json
),這樣可以保留未處理過的原始檔。
import os, json, csv
import requests
API = "https://jsonplaceholder.typicode.com/users"
OUT_DIR = "output"
# 工具函式
def fetch_users(timeout=10):
r = requests.get(API, timeout=timeout)
r.raise_for_status()
return r.json() # list[dict]
def save_json(data, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
步驟 2:扁平化巢狀資料
API 回傳的資料有巢狀結構,例如位址裡有 city,公司有 name。
把 address.city
變成單一欄位 city,company.name
變成單一欄位 company,方便後續處理。
def flatten_user(u: dict) -> dict:
"""把巢狀欄位攤平:address.city -> city, company.name -> company"""
def pick(d, path, default=None):
cur = d
for k in path.split("."):
if isinstance(cur, dict) and k in cur:
cur = cur[k]
else:
return default
return cur
return {
"id": u.get("id"),
"name": u.get("name"),
"username": u.get("username"),
"email": u.get("email"),
"city": pick(u, "address.city"),
"zipcode": pick(u, "address.zipcode"),
"phone": u.get("phone"),
"website": u.get("website"),
"company": pick(u, "company.name"),
}
步驟 3:資料清理
去除缺失值:關鍵欄位(id、name、email)不能缺,缺了要刪除整筆資料。
標準化:去除欄位多餘的空白字元,email 統一轉成小寫。
去除重複值:用 email 當「唯一鍵」,移除重複的資料(只保留第一筆)。
def is_empty(v) -> bool:
if v is None:
return True
if isinstance(v, str) and v.strip() == "":
return True
return False
def normalize_record(r: dict) -> dict:
"""字串去空白;email 小寫"""
out = {}
for k, v in r.items():
if isinstance(v, str):
v = v.strip()
out[k] = v
if out.get("email"):
out["email"] = out["email"].lower()
return out
def drop_missing_required(rows, required=("id", "name", "email")):
cleaned = []
for r in rows:
if all(not is_empty(r.get(k)) for k in required):
cleaned.append(r)
return cleaned
def deduplicate(rows, key="email"):
"""以 key 去重,保留第一筆"""
seen = set()
unique = []
for r in rows:
k = r.get(key)
if k not in seen:
seen.add(k)
unique.append(r)
return unique
步驟 4:匯出
把清理後的資料存成users_clean.csv
,之後可以給 Excel 或其他分析工具用。
也存成users_clean.json
,方便程式繼續使用。
def to_csv(rows, path, fieldnames):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", newline="", encoding="utf-8-sig") as f:
w = csv.DictWriter(f, fieldnames=fieldnames)
w.writeheader()
w.writerows(rows)
# 主流程
def main():
os.makedirs(OUT_DIR, exist_ok=True)
# 抓資料
try:
users = fetch_users()
except requests.exceptions.RequestException as e:
print("API 錯誤:", e)
return
# 存原始資料(未清理)
raw_path = os.path.join(OUT_DIR, "users_raw.json")
save_json(users, raw_path)
print(f"已存原始檔:{raw_path}({len(users)} 筆)")
# 模擬一點「髒資料」(為了示範清理,實務上這步不需要)
dirty = list(users)
if users:
import copy
dup = copy.deepcopy(users[0])
dup["name"] = " " + dup["name"] + " " # 多餘空白
dup["email"] = dup["email"].upper() # 大寫,會被標準化成小寫
dirty.append(dup) # 製造重複
miss = copy.deepcopy(users[1])
miss["id"] = 9999
miss["email"] = " " # 空白視為缺失
dirty.append(miss)
# 扁平化 → 標準化
flat = [flatten_user(u) for u in dirty]
normalized = [normalize_record(r) for r in flat]
# 去關鍵欄位缺失(id、name、email)
no_missing = drop_missing_required(normalized, required=("id","name","email"))
# 以 email 去重(也可改用 id)
unique = deduplicate(no_missing, key="email")
# 匯出清理後結果
fields = ["id","name","username","email","city","zipcode","phone","website","company"]
clean_csv = os.path.join(OUT_DIR, "users_clean.csv")
clean_json = os.path.join(OUT_DIR, "users_clean.json")
to_csv(unique, clean_csv, fields)
save_json(unique, clean_json)
print("—— 結果統計 ——")
print(f"原始筆數:{len(users)}")
print(f"加入髒資料後:{len(dirty)}")
print(f"扁平化後:{len(flat)}")
print(f"清掉缺失(id/name/email)後:{len(no_missing)}")
print(f"去重(email)後:{len(unique)}")
print(f"已輸出:{clean_csv} 與 {clean_json}")
if __name__ == "__main__":
main()
執行結果:
已存原始檔:output\users_raw.json(10 筆)
—— 結果統計 ——
原始筆數:10
加入髒資料後:12
扁平化後:12
清掉缺失(id/name/email)後:11
去重(email)後:10
已輸出:output\users_clean.csv 與 output\users_clean.json
學會了基礎資料清理:缺失值處理、重複值處理、資料標準化。
理解到API 資料不一定完整,要先做整理才好用。
完成了小實作:先抓 API,接著存 raw,然後清理,最後匯出乾淨版本。