用 Python 爬蟲抓取資料後,通常會將結果存成 JSON 格式。然而,Python 的 json.dumps() 預設行為會將非 ASCII 字元(包含中文、日文等)轉換成 \uXXXX 的 Unicode 跳脫序列,讓儲存的檔案難以直接閱讀。這篇文章說明問題原因與正確的解決方式。
問題重現
1
2
3
4
5
6
|
import json
data = {"artist": "じん"} # 日文字
result = json.dumps(data)
print(result)
# 輸出:{"artist": "\u3058\u3093"}
|
雖然程式讀取時不會有問題(JSON 規範允許 \uXXXX 表示法),但存到檔案後用文字編輯器開啟,會看到一堆跳脫序列,可讀性很差。
根本原因:ensure_ascii=True
json.dumps() 有一個參數 ensure_ascii,預設值為 True,這會讓所有非 ASCII 字元都被轉成 \uXXXX 格式,以確保輸出的 JSON 只包含 ASCII 字元(相容性最高)。
解決方式:ensure_ascii=False
1
2
3
4
5
6
|
import json
data = {"artist": "じん", "song": "人造エネミー"}
result = json.dumps(data, ensure_ascii=False)
print(result)
# 輸出:{"artist": "じん", "song": "人造エネミー"}
|
正確的 UTF-8 JSON 檔案讀寫
寫入 JSON 檔案
1
2
3
4
5
6
7
8
9
10
|
import json
data = [
{"name": "王小明", "age": 25},
{"name": "田中さん", "age": 30}
]
# 正確做法:同時設定 ensure_ascii=False 和 encoding='utf-8'
with open("output.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
|
json.dump()(不加 s)直接寫入檔案物件,json.dumps()(加 s)返回字串。
讀取 JSON 檔案
1
2
3
4
5
6
|
import json
with open("output.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(data[0]["name"]) # 王小明
|
Python 2 vs Python 3 的差異
| 面向 |
Python 2 |
Python 3 |
| 預設字串 |
bytes(str) |
Unicode(str) |
| Unicode 字串 |
需加 u 前綴:u"中文" |
直接使用 "中文" |
| 開啟檔案 |
需用 io.open() 指定 encoding |
open() 直接支援 encoding 參數 |
json.dumps() |
同樣預設 ensure_ascii=True |
同樣預設 ensure_ascii=True |
Python 2 的正確寫法(已不建議使用 Python 2):
1
2
3
4
5
6
7
8
|
# Python 2
import json, io
data = {u"name": u"王小明"}
result = json.dumps(data, ensure_ascii=False)
with io.open("output.json", "w", encoding="utf-8") as f:
f.write(result)
|
Python 3 的寫法更簡潔:
1
2
3
4
5
6
7
|
# Python 3(建議)
import json
data = {"name": "王小明"}
with open("output.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
|
其他格式化選項
1
2
3
4
5
6
|
json.dumps(data,
ensure_ascii=False, # 保留非 ASCII 字元
indent=2, # 縮排 2 格(美化輸出)
sort_keys=True, # 按鍵名排序
separators=(',', ':') # 壓縮輸出(去除空格)
)
|
處理換行符號
爬蟲抓到的文字常含有 \n、\r、\t 等控制字元,存入 JSON 時會被轉義,讀出時再還原,通常不需要特別處理。若確實需要保留字面上的反斜線,可用:
1
2
|
text = "第一行\n第二行"
# json.dumps 後:{"text": "第一行\n第二行"} ← \n 是跳脫序列,讀取時自動還原為換行
|
參考資料