消费账单分析
这个技能做什么、不做什么
- 做:把用户自己导出的账单流水(支付宝 / 微信支付 / 银行卡)读进来,识列、归类、去重,
- 不做:不联网(不查汇率、不查任何行情、不上传任何数据);
- 用户问「这钱该买点什么理财」「XX 基金能不能买」时,明确说这不在本技能范围内,
- 报告里出现的每一个数字,都能在用户自己的账单文件里追溯到具体某一行。
出一份月度报告——这个月花了多少、花在哪、比上个月多花了多少、有哪些还在扣的订阅、 哪几笔比自己的常态大得多。全是用户账单上已经发生的事实。
不碰投资理财(股票、基金、理财产品、保险、加密货币一概不在范围内); 不推荐任何产品、任何平台、任何消费方式;不给「你该怎么省钱」之类的建议。
把话题拉回「你的账单里这类支出是多少」。
隐私:全部在本地
- 账单里有商户、金额、对方姓名,属于高敏感数据。本技能只在本机读文件、在本机算、在本机写报告,
- 脚本只读用户在命令行里点名的文件,不扫描目录、不翻用户的其他文件。
- 输出只有两个文件:
bill-YYYY-MM.md和bill-YYYY-MM.json,写在--out指定的目录(默认当前目录)。 - 生成的报告本身也含隐私,往外发之前先提醒用户一句。
不发起任何网络请求,不把任何一行数据发到外部。
使用流程
- 让用户自己导出账单(别替用户去登录任何账号):
- 支付宝 App → 我的 → 账单 → 右上角 → 开具交易流水证明 / 导出账单 → 邮箱收 CSV
- 微信 → 我 → 服务 → 钱包 → 账单 → 常见问题 → 下载账单 → 邮箱收 CSV
- 网银 / 手机银行 → 交易明细 → 导出(CSV 或 xlsx)
- 先跑一次
--months,确认文件读对了、月份齐不齐:
`` python {baseDir}/scripts/bill.py <账单1> [账单2 ...] --months ``
- 出月报(不给
--month就是账单里最近的一个月):
`` python {baseDir}/scripts/bill.py <账单1> [账单2 ...] --month 2026-08 --out <输出目录> ``
- 看「疑似重复」那一节。确认那几对确实是同一笔钱之后,加
--dedup重跑,数字才准。 - 把报告原样展示给用户,再用两三句话点出最扎眼的一两个数字(只指方向,不下判断、不劝)。
命令
{baseDir} 是本技能目录。账单文件可以给多个,会合并成一份报告。
# 只看账单覆盖了哪些月份、每月多少(不出报告,适合先确认文件没读错)
python {baseDir}/scripts/bill.py 支付宝.csv 微信.csv 银行.csv --months
# 单文件月报(默认最近一个月,写到当前目录)
python {baseDir}/scripts/bill.py 微信.csv
# 多文件合并 + 指定月份 + 指定输出目录
python {baseDir}/scripts/bill.py 支付宝.csv 微信.csv 银行.xlsx --month 2026-08 --out ./报告
# 确认过疑似重复之后,真删重复笔再出一次
python {baseDir}/scripts/bill.py 支付宝.csv 微信.csv 银行.csv --dedup --out ./报告
# 用自己的分类规则表(覆盖内置规则)
python {baseDir}/scripts/bill.py 微信.csv --rules {baseDir}/assets/categories.example.json
# 不在终端刷报告正文,只要文件
python {baseDir}/scripts/bill.py 微信.csv --quiet --out ./报告
| 参数 | 作用 | |---|---| | 位置参数 | 账单文件,给几个都行(CSV / xlsx),自动合并 | | --month YYYY-MM | 要出的月份,默认账单里最近的一个月 | | --months | 只列出账单里有哪些月份和各月合计,不出报告 | | --rules <json> | 自定义分类规则表,命中优先级高于内置规则 | | --dedup | 真删疑似重复(默认只提示不删) | | --out <目录> | 报告输出目录,默认当前目录;写出 bill-YYYY-MM.md 和同名 .json | | --quiet | 不在终端打印报告正文,只打印两个文件路径 |
报告里有什么
| 小节 | 内容 | |---|---| | 一、总览 | 总支出 / 总收入 / 结余 / 笔数,各自与上月对比;笔均、日均;收入来源 | | 二、分类占比与环比 | 每个分类的金额、占比、笔数、笔均,以及与上月同分类的增减 | | 三、Top 10 单笔大额 | 本月最大的十笔支出,带日期、对方、说明、来自哪个文件 | | 四、Top 10 高频商户 | 按笔数排,看钱是被哪几家一点点磨走的 | | 五、周末 vs 工作日 | 两边的笔数、合计、日均(不是总额直接比,周末只有 8~10 天) | | 六、订阅型支出 | 连续 3 个月及以上「同一对方 + 同一金额」的支出,标出本月是否仍在扣、折合一年多少 | | 七、异常大额提示 | 单笔超过「该分类其余各笔笔均」3 倍的支出(该分类不足 3 笔时不判) | | 八、疑似重复 | 同一笔钱在两份账单里各记了一次的配对清单 | | 九、解析情况 | 每个文件认到的表头行、有效记录数、被跳过的行数、认到的列映射 |
分类
内置 14 个类目:餐饮、交通、购物、日用、居住、通讯、医疗、教育、娱乐、人情往来、转账、还款、收入、其他。
- 命中优先级:用户规则 > 内置规则 > 其他。
- 匹配范围是「交易对方 + 商品说明/摘要 + 备注 + 账单自带的交易分类」四项拼起来做包含匹配,
- 收入笔统一归到「收入」,除非用户规则明确另指一类。
- 自定义规则表见
assets/categories.example.json,两种写法都认:
不含支付方式那一列(不然「招商银行储蓄卡」会把一堆消费误判成银行相关)。
``json { "rules": [{"category": "宠物", "keywords": ["猫粮", "宠物医院"]}], "ignore_keywords": ["ATM取现"] } ``
或者最简单的 {"宠物": ["猫粮", "宠物医院"]}。 ignore_keywords 命中的行整行不计入统计,只在「解析情况」里报个数, 适合排掉自己往自己账户里搬钱的流水。
- 分类是关键词猜的,猜错很正常。用户说某笔归错了,就往规则表里加一条,别去改脚本。
去重口径
同一笔钱经常出现两次:微信里记「支付 128 给海底捞」,银行流水里记「财付通-消费 128」。判定条件(四条全中才算):
- 时间相差 ≤ 5 分钟;
- 金额完全相同;
- 收支方向相同;
- 来自不同的账单文件,且其中一方的文本里出现「微信 / 财付通 / 支付宝 / 云闪付 / 银联」等通道词。
保留信息多的那一条(写了商户名的微信/支付宝那侧),丢掉银行那侧的通道流水。 默认只在报告里列出来,不删——因为「同一天同样金额买了两次同一件东西」也完全可能是真的。 用户确认过之后再加 --dedup。
报告怎么说话
- 数字先行,不加形容词。说「本月餐饮 424.90 元,占 2.5%,5 笔」,
- 不劝、不教、不安慰。异常大额那一节只是「这笔和你自己的常态不一样」,不是「这笔花错了」。
- 不推荐任何商户、平台、支付方式、省钱办法、记账 App。
- 用户问「我这样花钱正常吗」,把分类占比那张表念出来,再说一句这只是他自己的数字,没有对照标准。
- 每份报告结尾固定一句:
不说「餐饮花得有点多」「这个月花超了」「要控制一下」。
以上全部来自你自己导出的账单文件,只做了归类和加总,没有联网取任何数据,也不构成任何消费或理财建议。 (脚本已自动附上,不要删。)
口径与边界
- 识列失败就停:前 30 行里找不到同时含「时间/日期」和「金额」的表头行时,脚本打印第一行并退出。
- 编码:CSV 依次尝试 utf-8-sig、utf-8、gbk、gb18030、utf-16;
- 表头位置:支付宝/微信/银行的导出前面都有几行说明文字,脚本自动往下找真正的列名行,
- 日期与时间分两列(银行常见):以日期列为准,把时间列拼回去;选中的列如果解析不出日期,
- 不计收支:支付宝/微信标「不计收支」的行(余额宝转入、零钱通转入之类)不进支出也不进收入,
- 交易关闭 / 失败 / 撤销 / 已全额退款的行直接跳过,并报个数。
- 退款:账单里标成「收入」的退款就按收入计,不去和原始那笔支出对冲(对冲不了,账单里没有配对关系)。
- 转账和还款算不算「花掉」:脚本把它们当支出统计,但单列成「转账」「还款」两类,
- 订阅识别只看「同对方 + 同金额 + 连续月份」,年付、变价、免密小额扣费认不出来;
- 异常大额用的是「该分类其余各笔的笔均」(把这笔自己剔掉再算均值),样本不足 3 笔的分类不判。
- 不做预算、不做目标、不做预测:没有「这个月还能花多少」,没有「照这个速度年底会花掉多少」。
- 不改用户的账单文件,一个字节都不写回去。
把表头发给用户确认,别猜,也别自己改数据文件。
xlsx 用标准库的 zipfile + xml 自己解(共享字符串、内联字符串、日期序列号都处理), 解析不了会明确报错让用户另存为 CSV;.xls 老格式不支持,直接报错。
并在报告里写明认的是第几行。
自动换一列能解析出来的。
只在「解析情况」里报个数。
用户想看「真实消费」时,让用户自己把这两类减掉——不要替用户定义什么叫真实消费。
账单只覆盖 1~2 个月时这一项本来就判不出来,报告里会直说。
脚本
| 脚本 | 用途 | |---|---| | scripts/common.py | 账单读取与解析(CSV/xlsx、多编码、找表头、列名模糊匹配、收支判定、分类、疑似重复) | | scripts/bill.py | 月度报告:总览、分类占比与环比、Top10、周末对比、订阅识别、异常大额、去重清单 |
纯 Python 3 标准库,零第三方依赖(xlsx 也是自己用 zipfile + xml 解的,不需要 openpyxl)。
assets/ 下有三份可直接跑的虚构样例(sample_alipay.csv、sample_wechat.csv、sample_bank.csv), 用来演示或自检;references/columns.md 是各家导出格式的列名对照。
# 自检:三份样例合并,应报出 3 对疑似重复、2 笔订阅
python {baseDir}/scripts/bill.py {baseDir}/assets/sample_alipay.csv \
{baseDir}/assets/sample_wechat.csv {baseDir}/assets/sample_bank.csv --dedup --out .