使用Python读取Apple Health数据

最近在学Python,就写了个小程序来玩玩。

注意事项:

1、使用Python 版本是2.7.12,推荐使用新一点的2.7.x版本,其他版本可能会有兼容问题。
2、画图用了matplotlib,在目录Python27\Scripts下直接pip install matplotlib就可以安装到对应的版本。
3、GUI用的是TKinter,用来实现几个按钮操作比较简单,学习成本低。
4、读取XML文件用lxml,之前用minidom读20M左右的xml文件要10秒,而且读取100M左右的文件就出现了Memory Error。这个lxml读取20M的文件只要2秒就ok,推荐使用,同样pip install lxml就ok了。

使用方法:

1、水果机里面有个健康应用,点进去。


image.png

2、右上角有个头像,点进去后,底部有个‘导出健康数据’,再点进去稍等一下,弹出一个界面


使用Python读取Apple Health数据_第1张图片
image.png

3、把导出的数据选拷贝至微信或者邮件什么的传到电脑上就会有一个导出.zip,解压后里面有一个导出.xml。
使用Python读取Apple Health数据_第2张图片
image.png

用文本编辑器打开导出.xml就发现里面记录了各种运动数据:


image.png

4、运行程序,点open file选中导出.xml
5、输入日期就会查询到特定日期下的所有数据,用图表显示出来。星号代表所有这个时间段的数据,比如在Month里面填星号,就会查到Year年的所有月的数据(如下图所示),在Day里面填*号则为查询这个月的所有数据,当然也可以查看某天的数据,这个纵轴Y代表的是换算为每分钟的步数,不是实际的步数量。

使用Python读取Apple Health数据_第3张图片
image.png

程序说明:

1、首先读取xml文件,然后把数据放到一个字典里面,字典的键值就是数据的日期,例如‘2017-11-13’,该天的所有数据为键值,把每项数据分别存在不同的列表中。
2、然后根据界面传来的参数作为读取日期,从字典里选出特定日期的数据,放在两个x、y列表中。
3、最后根据数据类型选择不同的坐标刻度,用matplotlib把数据画出来。

下面贴出完整的程序:

import datetime
import time
import tkFileDialog
from Tkinter import *

import matplotlib
import matplotlib.pyplot as plt
from lxml import etree
from matplotlib import dates as md
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

XMLFileName = []
ALL_DATA = {}


def ReadRecords(filename):
    tree = etree.parse(filename)
    records = tree.xpath('Record')
    for record in records:
        typeStr = record.get('type')
        if typeStr != 'HKQuantityTypeIdentifierStepCount':
            continue
        sourceNameStr = record.get('sourceName')
        startTimeStr = record.get('startDate')
        endTimeStr = record.get('endDate')
        try:
            stepVal = float(record.get('value'))
        except:
            stepVal = 0

        DateStr = startTimeStr[0:10]
        if DateStr not in ALL_DATA.keys():
            OneDay_DATA = {
                'type': [],
                'sourceName': [],
                'startDate': [],
                'endDate': [],
                'value': []
            }
            OneDay_DATA['type'].append(typeStr)
            OneDay_DATA['sourceName'].append(sourceNameStr)
            OneDay_DATA['startDate'].append(startTimeStr)
            OneDay_DATA['endDate'].append(endTimeStr)
            OneDay_DATA['value'].append(stepVal)
            ALL_DATA.update({DateStr: OneDay_DATA})
        else:
            ALL_DATA[DateStr]['type'].append(typeStr)
            ALL_DATA[DateStr]['sourceName'].append(sourceNameStr)
            ALL_DATA[DateStr]['startDate'].append(startTimeStr)
            ALL_DATA[DateStr]['endDate'].append(endTimeStr)
            ALL_DATA[DateStr]['value'].append(stepVal)


def showhealthdata():
    all_years = 0
    all_mons = 0
    all_days = 0
    year_num = 0
    mon_num = 0
    day_num = 0
    date = 'xxxx-xx-xx'
    total_steps = 0

    x = []
    y = []

    if year_entry_.get() == '*':
        all_years = 1
    else:
        try:
            year_num = int(year_entry_.get())
        except:
            year_num = 0
            print 'please input integer'
            year_entry_.delete(0, END)
            year_entry_.insert(0, '0')

    if mon_entry.get() == '*':
        all_mons = 1
    else:
        try:
            mon_num = int(mon_entry.get())
        except:
            mon_num = 0
            print 'please input integer'
            mon_entry.delete(0, END)
            mon_entry.insert(0, '0')

    if day_entry.get() == '*':
        all_days = 1
    else:
        try:
            day_num = int(day_entry.get())
        except:
            day_num = 0
            print 'please input integer'
            day_entry.delete(0, END)
            day_entry.insert(0, '0')

    if (all_years == 0 and all_mons == 0 and all_days == 0):
        date = '%04d-%02d-%02d' % (year_num, mon_num, day_num)
        if date not in ALL_DATA.keys():
            print 'No Record In:' + date
            return

    if all_years:
        x_formatter = md.DateFormatter('%y')
        x_locator = md.YearLocator()
        x_label = 'Year'
    elif all_mons:
        x_formatter = md.DateFormatter('%y-%m')
        x_locator = md.MonthLocator(interval=1)
        x_label = 'Month'
    elif all_days:
        x_formatter = md.DateFormatter('%m-%d')
        x_locator = md.DayLocator(interval=2)
        x_label = 'Day'
    else:
        x_formatter = md.DateFormatter('%H:%M')
        x_locator = md.MinuteLocator(interval=60)
        x_label = 'Hour'

    # key:'2018-01-01'
    for key in ALL_DATA:
       #year_in_key = int(key[0:4])
       #mon_in_key = int(key[5:7])
        if all_years:
            None
        elif all_mons:
            if int(key[0:4]) != year_num:
                continue
        elif all_days:
            if not(int(key[0:4]) == year_num and int(key[5:7]) == mon_num):
                continue
        else:
            key = date

        for i in range(len(ALL_DATA[key]['startDate'])):
            startTime = datetime.datetime.strptime(
                ALL_DATA[key]['startDate'][i][:-6], '%Y-%m-%d %H:%M:%S')
            endTime = datetime.datetime.strptime(
                ALL_DATA[key]['endDate'][i][:-6], '%Y-%m-%d %H:%M:%S')
            minutes = ((endTime - startTime).total_seconds()) / 60
            if minutes == 0:
                minutes = 1

            stepPerMin = int(ALL_DATA[key]['value'][i] / minutes)
            total_steps = total_steps + ALL_DATA[key]['value'][i]
            x.append(startTime)
            y.append(0)
            x.append(startTime)
            y.append(stepPerMin)
            x.append(endTime)
            y.append(stepPerMin)
            x.append(endTime)
            y.append(0)

        if key == date:
            break

    print total_steps
    showhealthdata.f.clf()
    showhealthdata.a = showhealthdata.f.add_subplot(111)

    showhealthdata.a.xaxis.set_major_formatter(x_formatter)
    showhealthdata.a.xaxis.set_major_locator(x_locator)
    showhealthdata.a.set_xlabel(x_label)
    showhealthdata.a.set_ylabel('Steps per minute')

    showhealthdata.a.plot(x, y)
    showhealthdata.a.get_figure().autofmt_xdate()

    showhealthdata.canvas.show()


def key_enter(event):
    if event.keycode == 13:
        showhealthdata()


def openxmlfile():
    XMLFileName = tkFileDialog.askopenfilename(
        filetypes=[("xml".decode('gbk'), "xml")], initialdir='C:')
    if XMLFileName:
        ALL_DATA.clear()
        print 'Reading...%s' % XMLFileName
        label_file.configure(text=XMLFileName)
        start = time.time()
        ReadRecords(XMLFileName)
        end = time.time()
        print 'Reading done'
        print 'Reading Records takes %f seconds' % (end - start)
        print '%d days' % len(ALL_DATA)


if __name__ == '__main__':
    matplotlib.use('TkAgg')
    root = Tk()
    # root.iconbitmap('ZKY.ico')
    root.title('ZKY')

    root.bind("", key_enter)

    showhealthdata.f = Figure(figsize=(8, 4), dpi=100)
    showhealthdata.canvas = FigureCanvasTkAgg(showhealthdata.f, master=root)
    showhealthdata.canvas.show()
    showhealthdata.canvas.get_tk_widget().grid(row=0, columnspan=10)

    label_year = Label(root, text='Year:')
    label_year.grid(row=1, column=0)
    year_entry_ = Entry(root)
    year_entry_.grid(row=1, column=1)
    year_entry_.insert(0, '0')

    label_mon = Label(root, text='Month:')
    label_mon.grid(row=1, column=2)
    mon_entry = Entry(root)
    mon_entry.grid(row=1, column=3)
    mon_entry.insert(0, '0')

    label_day = Label(root, text='Day:')
    label_day.grid(row=1, column=4)
    day_entry = Entry(root)
    day_entry.grid(row=1, column=5)
    day_entry.insert(0, '0')

    button_chk = Button(root, text='Check', command=showhealthdata)
    button_chk.grid(row=1, column=6)

    button_open = Button(root, text='Open File', command=openxmlfile)
    button_open.grid(row=2, column=0)

    label_file = Label(root, text=XMLFileName)
    label_file.grid(row=2, column=1, columnspan=10)

    root.mainloop()


你可能感兴趣的:(使用Python读取Apple Health数据)