scan_health.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. """
  2. 扫描 Apple Health export.xml,统计所有 Record type 和 Workout 的数量与时间范围。
  3. 流式解析,不吃内存。
  4. """
  5. import sys
  6. from collections import Counter
  7. from pathlib import Path
  8. from xml.etree.ElementTree import iterparse
  9. xml_path = Path(r"D:\workspace\Agi\zrd-nb666\campus_health_app\analytics\apple_health\apple_health_export\瀵煎嚭.xml")
  10. print(f"Parsing {xml_path.name} ({xml_path.stat().st_size/1024/1024:.1f} MB)...")
  11. record_types = Counter()
  12. workout_types = Counter()
  13. date_min = None
  14. date_max = None
  15. # 关心的类型样本
  16. samples = {}
  17. context = iterparse(str(xml_path), events=("end",))
  18. for event, elem in context:
  19. tag = elem.tag
  20. if tag == "Record":
  21. rtype = elem.get("type", "")
  22. record_types[rtype] += 1
  23. # 时间范围
  24. sd = elem.get("startDate", "")
  25. if sd:
  26. if date_min is None or sd < date_min:
  27. date_min = sd
  28. if date_max is None or sd > date_max:
  29. date_max = sd
  30. # 样本
  31. if rtype not in samples:
  32. samples[rtype] = {
  33. "source": elem.get("sourceName"),
  34. "value": elem.get("value"),
  35. "unit": elem.get("unit"),
  36. "date": sd,
  37. }
  38. elif tag == "Workout":
  39. wtype = elem.get("workoutActivityType", "")
  40. workout_types[wtype] += 1
  41. elem.clear()
  42. print(f"\n=== 时间范围 ===")
  43. print(f"最早: {date_min}")
  44. print(f"最新: {date_max}")
  45. print(f"\n=== Record 类型(共 {len(record_types)} 种)===")
  46. for rtype, cnt in record_types.most_common():
  47. s = samples.get(rtype, {})
  48. print(f"{cnt:>8} {rtype:60s} e.g. {s.get('value','')} {s.get('unit','')} via {s.get('source','')}")
  49. print(f"\n=== Workout 类型(共 {len(workout_types)} 种)===")
  50. for wtype, cnt in workout_types.most_common():
  51. print(f"{cnt:>8} {wtype}")