
简介这是一套面向健康数据爱好者、数字取证初学者及Python自动化实践者的小米手环数据提取工具集解决Zepp Life安卓应用本地数据库无法直接访问、原始健康指标难以结构化导出的痛点。资源共7个文件含6个Python脚本覆盖数据库解包、备份提取、压力数据解析、压缩包解压及SFTP远程获取等全流程和1个带详尽中文注释的DATE_DATA.json5配置文件便于快速理解字段含义与数据映射逻辑整体压缩包仅8KB轻量易部署。已有11103人学习下载说明其在手环数据自主分析、运动健康研究及移动端取证场景中具备较强实用性。使用者可直接运行脚本批量提取心率、睡眠、压力、步数等多维时序数据并基于json5格式灵活扩展分析逻辑无需逆向或Root设备显著降低健康数据二次利用门槛。1. 项目缘起为什么我们需要一个自动化导出工具如果你和我一样是个小米手环的长期用户大概率会遇到过这样的场景某天心血来潮想看看自己过去一年的运动趋势、睡眠质量变化或者想把手环里积累的宝贵数据导出来做个更深入的分析。这时候你打开小米运动健康App翻找半天发现官方只提供了非常有限的单日或单周数据查看想要批量导出几个月甚至几年的步数、心率、睡眠记录几乎是不可能的。要么只能对着手机屏幕一张张截图要么就彻底放弃。这种“数据被困在App里”的感觉相信很多注重数据记录的朋友都深有体会。我最初也是被这个问题困扰的。手环默默记录了海量的个人健康数据这些数据本应是了解自身状态、优化生活方式的绝佳材料却因为缺乏便捷的导出通道而变得难以利用。官方的数据生态相对封闭更侧重于App内的即时呈现和社交功能对于希望进行长期追踪、跨平台分析或建立个人健康数据库的用户来说并不友好。于是自己动手打造一个“小米手环数据自动化导出工具”的想法就诞生了。这个工具的核心目标很明确安全、稳定、无需人工干预地将小米运动健康App后端服务器上属于你自己的手环数据定期、完整地抓取下来并保存为结构化的、易于分析的格式如CSV或JSON。这不仅仅是一个简单的爬虫脚本。它涉及到对非官方API的反向工程、模拟登录以维持长期会话、处理复杂的数据加密与编码以及设计一套健壮的异常处理与日志机制确保在无人值守的情况下也能持续运行数月甚至数年。接下来我将详细拆解这个工具从构思到实现的完整过程分享其中遇到的技术挑战、解决方案以及那些官方文档里绝不会写的实操细节。2. 核心原理拆解数据从手环到我们手中的旅程在动手写代码之前我们必须先搞清楚小米手环的数据究竟是如何流转的。只有理解了这条链路才能找到合适的“介入点”。整个数据流可以概括为以下几个步骤手环传感器采集 - 手机蓝牙同步 - 小米运动健康App加密打包 - 小米云端服务器存储 - 我们的导出工具获取我们的工具其作用点就是在最后一步与小米云端服务器进行通信模拟官方App的行为请求并下载数据。这意味着我们不需要破解手环本身的蓝牙协议那复杂得多也不需要Root手机只需要在服务器层面与小米的API“对话”即可。2.1 关键环节认证与会话维持这是整个工具最核心也是最脆弱的一环。小米的API并非公开服务它需要合法的用户身份凭证通常是手机号密码或验证码登录后获得的Token来访问。我们的工具必须能完成登录流程并妥善管理登录后获得的会话状态如Cookies、Token。注意这里涉及的用户名和密码是你自己的小米账号。工具的设计原则必须是“本地化”或“自托管”即认证信息只存在于你运行脚本的设备上绝不传输到任何第三方服务器。这是数据安全和隐私的底线。登录成功后服务器会返回一个或多个Token例如userId,serviceToken,securityToken。这些Token具有时效性短则几小时长则数天或数周。我们的自动化工具必须能检测Token是否失效并在失效时自动重新登录。一种常见的策略是首次运行使用账号密码登录将获取到的Token持久化保存到本地文件后续运行时优先尝试使用保存的Token如果Token失效通过请求一个简单的用户信息接口如/user/profile来验证则触发重新登录流程。2.2 数据请求与解析一旦认证通过我们就可以模拟App请求数据了。这里需要借助一些技术手段来获知API的准确地址、参数格式和请求方法GET/POST。抓包分析这是最直接有效的方法。在电脑上设置代理如Charles或Fiddler将手机的Wi-Fi代理指向电脑然后在小米运动健康App中进行操作如查看“步数”详情页。此时抓包工具会捕获到手机与服务器之间的所有HTTP/HTTPS请求。我们需要从中筛选出那些看起来是请求历史数据的API。这些API的URL通常包含api-mifit.huami.com或类似域名路径可能包含/v1/data/、/report/、/list等关键词。参数解读观察抓包到的请求你会发现除了Token在请求头Header里请求体Body或查询参数Query中通常包含时间范围startTime,endTime、数据类型type、设备标识deviceId等。这些参数需要仔细记录和模仿。响应解析服务器返回的数据通常是JSON格式但有时可能会被额外编码或加密。你需要查看返回的原始数据理解其结构。例如步数数据可能是一个包含日期和步数字段的数组睡眠数据可能更复杂包含浅睡、深睡、清醒阶段的时间片列表。2.3 数据存储与格式化获取到原始的JSON数据后我们需要将其转换为更通用的格式。CSV逗号分隔值文件是最佳选择之一因为它可以被Excel、Google Sheets、Python pandas、R等几乎所有数据分析工具直接打开和处理。这一步的关键在于设计一个清晰的数据表结构。例如对于步数数据可以创建steps.csv包含列date日期total_steps总步数calories卡路里distance距离。对于睡眠数据可以创建sleep.csv包含列date日期start_time开始时间end_time结束时间deep_sleep_minutes深睡分钟数light_sleep_minutes浅睡分钟数awake_minutes清醒分钟数sleep_score睡眠分数。工具应该能够将每次运行获取的新数据追加Append到已有的CSV文件中而不是覆盖这样就能形成一个持续增长的个人健康数据库。3. 技术选型与实现框架基于上述原理我选择使用Python作为开发语言。原因很简单Python在数据处理、HTTP请求、定时任务等方面有极其丰富的库支持开发效率高并且易于部署在各种环境Windows, macOS, Linux 甚至树莓派。下面是一个简化的核心模块设计认证模块 (Auth)负责处理登录逻辑管理Token的获取、刷新、保存和加载。使用requests库发起HTTP请求使用json库处理响应。数据获取模块 (Fetcher)负责构造针对不同数据类型步数、心率、睡眠、体重等的API请求发送请求并接收响应。需要根据抓包结果精确还原请求头和请求体。数据解析与存储模块 (Parser Storage)负责将API返回的JSON数据解析成结构化的Python对象如字典列表然后将其转换为Pandas DataFrame最后写入CSV文件。使用pandas库可以极大地简化这个过程。调度与日志模块 (Scheduler Logger)负责让整个流程定期自动执行例如每天凌晨2点运行一次。可以使用操作系统自带的cronLinux/macOS或任务计划程序Windows也可以使用Python的schedule或APScheduler库在脚本内部实现。同时需要一个完善的日志系统使用Python内置的logging模块记录每次运行的成功与否、获取的数据量、遇到的错误等便于后期监控和排错。一个最基本的项目目录结构可能如下所示mi_band_exporter/ ├── config.yaml # 配置文件存放账号、时间间隔、数据存储路径等 ├── auth.py # 认证模块 ├── fetcher.py # 数据获取模块 ├── parser.py # 数据解析模块 ├── storage.py # 数据存储模块 ├── scheduler.py # 调度模块如果用内部调度 ├── main.py # 主程序入口 ├── tokens.json # 保存的Token文件.gitignore忽略 ├── logs/ # 日志目录 │ └── exporter.log └── data/ # 导出的数据目录 ├── steps.csv ├── heart_rate.csv └── sleep.csv4. 实操步骤详解与核心代码片段接下来我们深入到代码层面。请注意以下代码仅为示例和思路演示因为小米的API接口细节可能随时变更且涉及隐私不便提供完整可用的密钥。4.1 环境准备与依赖安装首先创建一个干净的Python虚拟环境并安装必要的包。# 创建并激活虚拟环境以venv为例 python -m venv venv # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 安装核心依赖 pip install requests pandas schedule # requests: 用于网络请求 # pandas: 用于数据处理和CSV读写 # schedule: 用于简单的内部任务调度可选4.2 认证模块实现这是最复杂的一步。我们需要模拟App的登录过程。通过抓包你可能会发现登录流程涉及多个步骤和重定向。# auth.py import requests import json import time from typing import Optional, Dict import logging logger logging.getLogger(__name__) class MiFitAuth: def __init__(self, config_path: str config.yaml): self.session requests.Session() self.session.headers.update({ User-Agent: MiFit/4.6.0 (iPhone; iOS 14.4; Scale/3.00), # 模拟iOS App Content-Type: application/x-www-form-urlencoded, }) self.load_config(config_path) self.tokens self.load_tokens() def load_config(self, path): # 从YAML或JSON配置文件读取账号密码 # 这里简化为字典 self.config { username: 你的小米账号手机号, password: 你的密码, login_api: https://account.xiaomi.com/pass/serviceLoginAuth2 # 示例地址需抓包确认 } def load_tokens(self) - Dict: try: with open(tokens.json, r) as f: return json.load(f) except FileNotFoundError: return {} def save_tokens(self, tokens: Dict): with open(tokens.json, w) as f: json.dump(tokens, f, indent2) def is_token_valid(self) - bool: 验证当前Token是否有效 if not self.tokens.get(user_id) or not self.tokens.get(service_token): return False # 尝试请求一个需要认证的简单接口如用户信息 test_url https://api-mifit.huami.com/v1/user/profile headers {X-Token: self.tokens.get(service_token)} try: resp self.session.get(test_url, headersheaders, timeout10) return resp.status_code 200 and json.loads(resp.text).get(code) 0 except Exception as e: logger.error(fToken验证失败: {e}) return False def login(self) - bool: 执行登录流程获取并保存Token logger.info(开始登录流程...) # 注意以下参数名和值都需要根据实际抓包结果填充这里仅为示意 login_data { user: self.config[username], hash: self._calculate_password_hash(self.config[password]), # 密码通常不是明文传输需要计算哈希或加密 sid: mifit, # 服务标识 callback: https://api-mifit..., # 回调地址 _sign: self._generate_signature(...), # 签名防止篡改 # ... 其他必要参数 } try: resp self.session.post(self.config[login_api], datalogin_data) resp_data json.loads(resp.text) if resp_data.get(code) 0: # 解析响应提取关键Token new_tokens { user_id: resp_data[userId], service_token: resp_data[serviceToken], security_token: resp_data.get(securityToken), login_time: int(time.time()) } self.tokens new_tokens self.save_tokens(new_tokens) logger.info(登录成功Token已保存。) return True else: logger.error(f登录失败: {resp_data.get(message)}) return False except Exception as e: logger.error(f登录请求异常: {e}) return False def _calculate_password_hash(self, password: str) - str: 模拟App计算密码哈希的方法具体算法需逆向分析 # 这是一个复杂且可能变动的点。可能需要使用特定的盐salt和哈希算法如SHA1, MD5。 # 一种常见方法是hash md5(salt md5(password)) # 此处省略具体实现需通过逆向工程或动态调试获得。 return 需要根据逆向分析实现的哈希值 def get_auth_headers(self) - Dict: 获取用于API请求的认证头 if not self.is_token_valid(): if not self.login(): raise Exception(无法获取有效的认证Token) return { X-Token: self.tokens[service_token], X-User-Id: self.tokens[user_id], # 可能还需要其他Header如X-App-Version, X-Device-Id等 }核心难点与心得登录模块的_calculate_password_hash和_generate_signature函数是整个项目的“钥匙”。小米App为了安全会对密码和请求参数进行复杂的加密和签名。破解这个环节通常需要逆向工程安卓或iOS的App安装包分析其加密逻辑。这对于普通开发者门槛较高。一个更简单但可能不稳定的替代方案是使用验证码登录流程。有些API接口支持通过短信验证码登录这种方式无需破解密码加密算法只需要模拟获取和提交验证码的流程。不过这需要额外的步骤来处理验证码的接收例如在电脑端登录时手机收到验证码后手动输入或通过某些自动化方式读取。4.3 数据获取模块实现假设我们已经通过抓包找到了获取步数历史的API。# fetcher.py import requests import json import time from datetime import datetime, timedelta from auth import MiFitAuth class DataFetcher: def __init__(self, auth: MiFitAuth): self.auth auth self.base_url https://api-mifit.huami.com def fetch_steps(self, date_str: str) - Optional[Dict]: 获取指定日期的步数数据 # date_str 格式2023-10-27 url f{self.base_url}/v1/data/step.json # 示例路径 headers self.auth.get_auth_headers() params { queryDate: date_str, deviceType: 手环设备类型ID, # 需从抓包或用户信息接口获取 # ... 其他必要参数 } try: resp requests.get(url, headersheaders, paramsparams, timeout30) data resp.json() if data.get(code) 0: logger.info(f成功获取 {date_str} 的步数数据) return data.get(data, {}) else: logger.warning(f获取 {date_str} 步数数据失败: {data.get(message)}) return None except Exception as e: logger.error(f请求步数API异常 ({date_str}): {e}) return None def fetch_sleep(self, date_str: str) - Optional[Dict]: 获取指定日期的睡眠数据 url f{self.base_url}/v1/data/sleep.json headers self.auth.get_auth_headers() # 睡眠数据可能需要起始和结束时间戳 date_obj datetime.strptime(date_str, %Y-%m-%d) start_time int(date_obj.timestamp() * 1000) # 转换为毫秒时间戳 end_time int((date_obj timedelta(days1)).timestamp() * 1000) - 1 params { startTime: start_time, endTime: end_time, deviceType: 手环设备类型ID, } # ... 发送请求并解析响应 pass def fetch_heart_rate(self, date_str: str, sample_interval: str 1min) - Optional[Dict]: 获取指定日期的心率数据sample_interval可以是1min, 5min等 # 心率数据通常是高频采样数据量较大API可能分页或按时间段返回 pass4.4 数据解析与存储模块实现# storage.py import pandas as pd from datetime import datetime import os class DataStorage: def __init__(self, data_dir: str ./data): self.data_dir data_dir os.makedirs(data_dir, exist_okTrue) def save_steps_to_csv(self, steps_data_list: list, date_str: str): 将步数数据列表保存或追加到CSV文件 if not steps_data_list: return # 将原始数据转换为DataFrame # 假设steps_data_list是 [{date:2023-10-27, steps: 8523, calories: 420}, ...] df_new pd.DataFrame(steps_data_list) file_path os.path.join(self.data_dir, steps.csv) if os.path.exists(file_path): # 读取现有文件 df_existing pd.read_csv(file_path) # 合并新旧数据并去重基于日期 df_combined pd.concat([df_existing, df_new]).drop_duplicates(subset[date], keeplast).sort_values(bydate) df_combined.to_csv(file_path, indexFalse) print(f已更新 steps.csv 新增 {len(df_new)} 条记录。) else: # 首次创建文件 df_new.to_csv(file_path, indexFalse) print(f已创建 steps.csv 保存 {len(df_new)} 条记录。) def save_sleep_to_csv(self, sleep_data_list: list): 保存睡眠数据睡眠数据结构可能更复杂 # 睡眠数据可能包含夜间多次醒来或者午睡。 # 需要根据API返回的格式设计合适的DataFrame结构。 # 例如[date, start_time, end_time, deep_sleep_min, light_sleep_min, awake_min, total_min, score] pass4.5 主程序与调度逻辑# main.py import schedule import time from datetime import datetime, timedelta from auth import MiFitAuth from fetcher import DataFetcher from storage import DataStorage import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(logs/exporter.log), logging.StreamHandler() ]) logger logging.getLogger(__name__) def daily_job(): 每天执行的任务 logger.info(开始每日数据导出任务...) auth MiFitAuth() fetcher DataFetcher(auth) storage DataStorage() # 获取昨天的数据因为今天的数据可能还不完整 target_date (datetime.now() - timedelta(days1)).strftime(%Y-%m-%d) logger.info(f目标日期: {target_date}) # 1. 获取步数 steps_data fetcher.fetch_steps(target_date) if steps_data: # 这里需要根据API实际返回结构将数据转换成storage需要的列表格式 processed_steps [{date: target_date, steps: steps_data.get(steps, 0)}] storage.save_steps_to_csv(processed_steps, target_date) # 2. 获取睡眠 # sleep_data fetcher.fetch_sleep(target_date) # if sleep_data: ... # 3. 获取心率可选数据量大 # heart_rate_data fetcher.fetch_heart_rate(target_date, 5min) # if heart_rate_data: ... logger.info(每日数据导出任务完成。) if __name__ __main__: # 方法一使用schedule库进行内部调度适合长期运行的脚本 schedule.every().day.at(02:30).do(daily_job) # 每天凌晨2:30运行 logger.info(调度器已启动将在每天02:30运行任务。按 CtrlC 退出。) while True: schedule.run_pending() time.sleep(60) # 每分钟检查一次 # 方法二直接运行一次适合配合系统cron使用 # daily_job()5. 部署、监控与长期维护的实战经验将代码跑起来只是第一步要让这个工具稳定运行数月甚至数年还需要考虑很多工程化问题。5.1 部署环境选择个人电脑最简单但电脑需要常年开机且可能因系统更新、休眠而中断。家庭服务器/NAS如群晖DSM可以在Docker容器或任务计划中运行Python脚本非常稳定。云服务器最可靠但需要一定成本。可以选择最低配置的Linux云服务器如1核1G使用systemd或supervisor来管理进程配合cron定时任务。树莓派性价比极高的选择功耗低可7x24小时运行是完美的家庭自动化节点。我个人的选择是部署在家庭NAS的Docker容器里。这样既保证了持续运行又便于管理镜像打包、日志挂载、配置持久化。5.2 异常处理与健壮性增强最初的脚本可能一遇到网络波动、API变更或Token失效就会崩溃。我们必须增强其健壮性。重试机制对于网络请求失败应该加入指数退避的重试逻辑。from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def fetch_data_with_retry(url, headers, params): response requests.get(url, headersheaders, paramsparams, timeout45) response.raise_for_status() return response.json()Token自动刷新在DataFetcher的每次请求前都通过auth.get_auth_headers()来获取Headers而这个方法内部已经集成了Token有效性检查和自动重新登录。确保认证状态总是最新的。数据完整性校验在保存数据前检查获取的数据是否包含必要的字段日期格式是否正确。对于心率这类大数据检查数据点数量是否在合理范围内例如一天24小时5分钟间隔应有288个点如果只拿到10个可能出错了。详细的日志记录日志不仅要记录成功和失败还要记录关键操作的数据摘要例如“成功获取2023-10-27步数8523步”、“登录失败原因为密码错误”。这能让你在出现问题时快速定位。5.3 应对API变更这是所有依赖非官方接口的工具最大的风险。小米可能随时更新其App和后台API导致我们的工具失效。监控在日志中增加对特定错误码的监控。例如如果连续多次请求都返回code: 5假设5代表接口废弃则触发报警如发送一封邮件到自己的邮箱。配置化将API的URL、参数名等提取到配置文件如config.yaml中而不是硬编码在代码里。当API变更时你只需要更新配置文件而无需修改核心代码逻辑。定期手动检查每隔一两个月手动运行一次脚本或者用抓包工具看看最新的App通信格式是否有变化。5.4 数据备份与扩展导出的CSV文件是你的宝贵资产。建议定期例如每周将data/目录备份到云盘或其他安全位置。此外这个工具的框架是通用的。一旦跑通了步数数据你可以用同样的模式去扩展支持更多数据类型心率通常有详细到每分钟的静息心率和运动心率。睡眠包含睡眠阶段划分。体重/体脂如果你有小米体脂秤并关联了同一账号。运动记录每次跑步、游泳的GPS轨迹和详细数据。压力、血氧等取决于手环型号。每增加一种数据类型就相当于为你的个人健康数据库增加了一个新的维度。打造这样一个自动化导出工具的过程更像是一次有趣的探险。你不仅得到了一个解放双手、掌控自身数据的实用工具更深入理解了移动应用与云端服务交互的细节。当你能随时用自己熟悉的工具如Jupyter Notebook, Tableau分析自己多年的运动睡眠趋势时那种成就感和对自身生活的洞察是任何现成App都无法提供的。最重要的是整个过程都在你自己的控制之下数据始终留在本地安全且私密。本文还有配套的精品资源点击获取