用 AI 分析你的 Apple Watch 健康數據:從匯出到洞察的完整教程
用 AI 分析你的 Apple Watch 健康數據:從匯出到洞察的完整教程
Apple Watch 每天默默記錄你的心率、睡眠、步數、血氧……但大多數人只是偶爾瞄一眼 App,從未真正「讀懂」這些數據。這篇教程教你把健康數據完整匯出,用 Python 整理成 CSV,再交給 AI 做深度分析。
目錄
第一步:從 iPhone 匯出健康數據
- 打開 iPhone 上的「健康」App
- 點右上角的頭像
- 滑到頁面最底部
- 點「匯出所有健康資料」
- 等待打包完成(依資料量需要 1~5 分鐘)
- 選擇儲存位置(建議選 iCloud 雲碟,方便後續在電腦下載)
匯出的 export.zip 通常有幾百 MB,請確保手機和 iCloud 空間足夠。第二步:把 export.zip 傳到電腦
根據你的環境選擇最方便的方式:
| 方式 | 適合情境 |
|---|---|
| iCloud Drive | 最簡單,匯出時直接存到雲碟,電腦端下載 |
| 電子郵件 | 寄到自己信箱,電腦下載附件 |
| USB + iTunes | 有線傳輸,速度最快 |
| AirDrop | 僅限 Mac,Windows 不支援 |
第三步:安裝 Python
Windows
- 前往 python.org/downloads
- 點 Download Python 3.x.x
執行安裝程式時,務必勾選「Add Python to PATH」
☑ Add Python to PATH ← 這個一定要勾!
macOS
macOS 通常已內建 Python 3,打開「終端機」輸入以下指令確認:
python3 --version若未安裝,同樣前往 python.org 下載,或使用 Homebrew:
brew install python第四步:執行轉換腳本
下載腳本
將以下腳本儲存為 apple_health_to_csv.py:
#!/usr/bin/env python3
"""
Apple Health Export → CSV 轉換工具
支援:心率、步數、活動消耗、心電圖、血氧、睡眠、體重等所有指標
用法:python apple_health_to_csv.py [export.zip 或 export.xml 路徑]
"""
import zipfile
import xml.etree.ElementTree as ET
import csv
import os
import sys
import re
from collections import defaultdict
from datetime import datetime
RECORD_TYPES = {
"HKQuantityTypeIdentifierHeartRate": ("heart_rate", ["date", "time", "value_bpm", "device"]),
"HKQuantityTypeIdentifierHeartRateVariabilitySDNN": ("hrv", ["date", "time", "value_ms", "device"]),
"HKQuantityTypeIdentifierRestingHeartRate": ("resting_heart_rate",["date", "value_bpm", "device"]),
"HKQuantityTypeIdentifierWalkingHeartRateAverage": ("walking_heart_rate",["date", "value_bpm", "device"]),
"HKQuantityTypeIdentifierOxygenSaturation": ("blood_oxygen", ["date", "time", "value_pct", "device"]),
"HKQuantityTypeIdentifierStepCount": ("steps", ["date", "start_time", "end_time", "value_steps", "device"]),
"HKQuantityTypeIdentifierDistanceWalkingRunning": ("distance_walk_run", ["date", "start_time", "end_time", "value_km", "device"]),
"HKQuantityTypeIdentifierActiveEnergyBurned": ("active_energy", ["date", "start_time", "end_time", "value_kcal", "device"]),
"HKQuantityTypeIdentifierBasalEnergyBurned": ("basal_energy", ["date", "start_time", "end_time", "value_kcal", "device"]),
"HKQuantityTypeIdentifierAppleExerciseTime": ("exercise_time", ["date", "start_time", "end_time", "value_min", "device"]),
"HKQuantityTypeIdentifierAppleStandTime": ("stand_time", ["date", "start_time", "end_time", "value_min", "device"]),
"HKQuantityTypeIdentifierBodyMass": ("body_mass", ["date", "value_kg", "device"]),
"HKQuantityTypeIdentifierBodyFatPercentage": ("body_fat", ["date", "value_pct", "device"]),
"HKQuantityTypeIdentifierBodyMassIndex": ("bmi", ["date", "value", "device"]),
"HKQuantityTypeIdentifierBloodPressureSystolic": ("bp_systolic", ["date", "time", "value_mmhg", "device"]),
"HKQuantityTypeIdentifierBloodPressureDiastolic": ("bp_diastolic", ["date", "time", "value_mmhg", "device"]),
"HKQuantityTypeIdentifierBloodGlucose": ("blood_glucose", ["date", "time", "value_mmol", "device"]),
"HKQuantityTypeIdentifierRespiratoryRate": ("respiratory_rate", ["date", "time", "value_bpm", "device"]),
"HKQuantityTypeIdentifierVO2Max": ("vo2max", ["date", "value_ml_kg_min", "device"]),
"HKQuantityTypeIdentifierWalkingSpeed": ("walking_speed", ["date", "value_km_h", "device"]),
"HKQuantityTypeIdentifierEnvironmentalAudioExposure": ("env_audio", ["date", "time", "value_db", "device"]),
"HKQuantityTypeIdentifierBodyTemperature": ("body_temp", ["date", "time", "value_c", "device"]),
"HKQuantityTypeIdentifierAppleSleepingWristTemperature": ("wrist_temp", ["date", "time", "value_c", "device"]),
}
def parse_dt(s):
s = re.sub(r'\s[+-]\d{4}$', '', s.strip())
try:
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
return dt.strftime("%Y-%m-%d"), dt.strftime("%H:%M:%S")
except ValueError:
return s[:10], s[11:19]
def get_device(elem):
full_device = elem.get("device", "")
if "Apple Watch" in full_device:
return "Apple Watch"
elif "iPhone" in full_device:
return "iPhone"
return elem.get("sourceName", "unknown")[:30]
def parse_sleep(elem, rows):
stage_map = {
"HKCategoryValueSleepAnalysisInBed": "in_bed",
"HKCategoryValueSleepAnalysisAsleepUnspecified":"asleep",
"HKCategoryValueSleepAnalysisAwake": "awake",
"HKCategoryValueSleepAnalysisAsleepCore": "core",
"HKCategoryValueSleepAnalysisAsleepDeep": "deep",
"HKCategoryValueSleepAnalysisAsleepREM": "rem",
}
stage = stage_map.get(elem.get("value", ""), elem.get("value", ""))
start_date, start_time = parse_dt(elem.get("startDate", ""))
_, end_time = parse_dt(elem.get("endDate", ""))
rows.append([start_date, start_time, end_time, stage, get_device(elem)])
def parse_workout(elem, rows):
act_type = elem.get("workoutActivityType", "").replace("HKWorkoutActivityType", "")
start_date, start_time = parse_dt(elem.get("startDate", ""))
_, end_time = parse_dt(elem.get("endDate", ""))
duration = elem.get("duration", "")
energy, distance = "", ""
for stat in elem.findall("WorkoutStatistics"):
t = stat.get("type", "")
if "ActiveEnergy" in t:
energy = stat.get("sum", "")
if "Distance" in t:
distance = stat.get("sum", "")
rows.append([start_date, start_time, end_time, act_type,
round(float(duration), 2) if duration else "",
round(float(energy), 2) if energy else "",
round(float(distance) / 1000, 4) if distance else "",
get_device(elem)])
def parse_ecg(elem, ecg_rows, voltage_rows):
date, time_ = parse_dt(elem.get("startDate", ""))
ecg_rows.append([date, time_, elem.get("classification",""),
elem.get("averageHeartRate",""), elem.get("symptomsStatus",""), get_device(elem)])
for vp in elem.findall("VoltageMeasurement"):
voltage_rows.append([date, time_, vp.get("timeSinceSampleStart",""), vp.get("leadVoltage","")])
def main():
source = sys.argv[1] if len(sys.argv) > 1 else next(
(p for p in ["export.zip", "export.xml"] if os.path.exists(p)), None)
if not source:
print(" 找不到 export.zip 或 export.xml")
sys.exit(1)
output_dir = "health_csv"
os.makedirs(output_dir, exist_ok=True)
record_data = defaultdict(list)
sleep_rows, workout_rows, ecg_rows, voltage_rows = [], [], [], []
total = 0
print(f" 解析中:{source}")
xml_file = zipfile.ZipFile(source).open(
next(n for n in zipfile.ZipFile(source).namelist() if n.endswith("export.xml"))
) if source.endswith(".zip") else open(source, "rb")
for event, elem in ET.iterparse(xml_file, events=("end",)):
total += 1
if elem.tag == "Record":
rtype = elem.get("type", "")
if rtype == "HKCategoryTypeIdentifierSleepAnalysis":
parse_sleep(elem, sleep_rows)
elif rtype in RECORD_TYPES:
info = RECORD_TYPES[rtype]
try:
val = round(float(elem.get("value", "")), 4)
except:
val = elem.get("value", "")
d, t = parse_dt(elem.get("startDate", ""))
_, et = parse_dt(elem.get("endDate", elem.get("startDate","")))
dev = get_device(elem)
if len(info[1]) == 4:
record_data[rtype].append([d, t, val, dev])
else:
record_data[rtype].append([d, t, et, val, dev])
elif elem.tag == "Workout":
parse_workout(elem, workout_rows)
elif elem.tag == "Electrocardiogram":
parse_ecg(elem, ecg_rows, voltage_rows)
elem.clear()
if total % 100000 == 0:
print(f" 已處理 {total:,} 筆...")
def write_csv(name, headers, rows):
if not rows:
return
path = os.path.join(output_dir, f"{name}.csv")
with open(path, "w", newline="", encoding="utf-8-sig") as f:
csv.writer(f).writerow(headers)
csv.writer(f).writerows(rows)
print(f" {name}.csv — {len(rows):,} 筆")
for rtype, rows in record_data.items():
info = RECORD_TYPES[rtype]
write_csv(info[0], info[1], rows)
write_csv("sleep", ["date","start_time","end_time","stage","device"], sleep_rows)
write_csv("workouts", ["date","start_time","end_time","activity_type","duration_min","energy_kcal","distance_km","device"], workout_rows)
write_csv("ecg_summary", ["date","time","classification","avg_heart_rate","symptoms","device"], ecg_rows)
write_csv("ecg_voltage", ["date","time","time_since_start_ms","voltage_uV"], voltage_rows)
print(f"\n 完成!CSV 已儲存至 ./{output_dir}/")
if __name__ == "__main__":
main()準備文件
把腳本和 export.zip 放在同一個資料夾:
C:\Users\你的名字\Desktop\health\
├── apple_health_to_csv.py
└── export.zip執行(Windows)
在資料夾空白處按住 Shift + 右鍵,選「在此處開啟 PowerShell 視窗」,輸入:
python apple_health_to_csv.py export.zip執行(macOS / Linux)
cd ~/Desktop/health
python3 apple_health_to_csv.py export.zip輸出結果
執行完成後,同目錄會出現 health_csv/ 資料夾:
| 檔案 | 內容 |
|---|---|
heart_rate.csv | 每次心率測量 |
hrv.csv | 心率變異性(HRV) |
blood_oxygen.csv | 血氧 SpO2 |
steps.csv | 步數 |
active_energy.csv | 活動消耗卡路里 |
basal_energy.csv | 基礎代謝消耗 |
sleep.csv | 睡眠分期(深睡 / REM / 淺眠) |
workouts.csv | 運動紀錄 |
ecg_summary.csv | 心電圖結論 |
ecg_voltage.csv | 心電圖電壓原始點 |
vo2max.csv | 最大攝氧量 |
body_mass.csv | 體重 |
ecg_voltage.csv 包含每筆心電圖的原始電壓點,檔案可能很大,分析時可視情況略過。第五步:上傳 CSV 給 AI 分析
打開 claude.ai,把 health_csv/ 裡的 CSV 直接拖入對話框,搭配以下提示詞開始分析。
常見問題
Q:腳本執行時出現錯誤怎麼辦?
把完整錯誤訊息複製,貼給 AI 詢問,通常能快速定位問題。
Q:export.zip 很大,要全部上傳嗎?
不需要。建議先上傳你最關心的 2~3 個 CSV(如 heart_rate.csv + sleep.csv),分析完再補充其他指標。
Q:數據安全嗎?
健康數據屬於敏感個人資訊。如有顧慮,可在上傳前刪除 device 欄位或對日期進行偏移處理。