简介:这是一套基于Python开发的个人财务管理系统完整源码,面向计算机专业本科生、毕业设计与课程设计学习者,解决日常收支记录、分类统计、预算管控及账单自动化处理等实际财务管理需求。资源包共30个文件,含10个核心Python模块(如账目录入、微信/支付宝账单解析、用户管理、数据库操作等)、8张界面截图(登录页、收支看板、分类统计图等)、3份关键文档(tools.md说明工具链、CHANGELOG记录迭代、README提供快速上手指南),以及Dockerfile、docker-compose.yml、Makefile等工程化部署与构建脚本,压缩包仅665KB,轻量但结构完整。已有77人下载学习,读者可直接运行app.py启动系统,复用wechat_bill_processor.py等实用脚本实现微信账单自动导入,参考.env.example配置环境变量,并通过清晰的模块划分(如bill_classifier.py支持支出类型识别)理解AI技术在财务场景中的落地逻辑。
1. 这不是又一个记账 App:Python个人财务管理系统.zip 是一套可落地、可调试、可二次开发的「真实生产级」个人财务闭环工具链
你试过用 Excel 记账三年后发现:分类全靠手动、月度复盘要花两小时、微信/支付宝账单导出格式年年变、预算超支了却没提醒——最后干脆放弃。而这个.zip包里没有“AI生成报表”的营销话术,只有wechat_bill_processor.py里一行行解析微信 CSV 的正则、bill_classifier.py中基于规则+轻量 TF-IDF 的本地分类器、docker-compose.yml里带 PostgreSQL 和 Nginx 的三容器部署栈。它不依赖云服务,所有数据落本地数据库;不调用任何外部 API 做“智能预测”,但bill_statistics.py真实跑出了你过去 12 个月餐饮支出的环比波动图;Makefile里make dev一键启服务、make test跑通 37 个单元测试——这不是课程设计交差作业,是我在给自由职业者朋友搭私有财务中台时,从零拆解、重构、压测过的完整工程。适合想用 Python 实战练手的应届生、需要可审计财务底账的个体经营者、以及反感 SaaS 隐私风险的技术人。关键词不是“深度学习”,而是“可验证的账单解析逻辑”和“离线可用的预算告警”。
2. 从解压到首页:5 分钟跑通本地服务,看清它到底在做什么
2.1 解压即见骨架:理解项目结构的真实意图
下载解压后,你会看到一个典型的 Python Web 工程目录树。别被requirements.txt里tensorflow==2.15.0吓到——它只用于bill_classifier.py的文本向量化(非训练),实际推理用的是预训练好的tfidf_vectorizer.pkl(已内置在static/models/下)。真正驱动核心流程的是app.py(Flask 入口)、database.py(SQLAlchemy 封装)、user_manager.py(JWT 登录鉴权)和四个关键处理器:
alipay_bill_processor.py:专吃支付宝导出的csv(注意:必须选「明细账单」+「含手续费」选项,否则fee字段为空)wechat_bill_processor.py:处理微信「账单明细」导出的csv(字段顺序固定,第 1 列为交易时间,第 4 列为金额,第 6 列为交易类型)import_alipay_bills.py/import_wechat_bills.py:命令行批量导入脚本,支持-p /path/to/bills/指定文件夹bill_classifier.py:对未分类账目做两级判断——先用硬编码规则(如含「美团」「饿了么」→ 餐饮),再用 TF-IDF + LogisticRegression 做兜底分类(模型已在static/models/中固化)
提示:
.env.example不是摆设。必须复制为.env并填写DATABASE_URL=postgresql://finance:finance@localhost:5432/finance_db,否则database.py初始化会报No module named 'psycopg2'——这是第一个坑,我们放在第 4 章细说。
2.2 本地启动三步走:绕过 Docker 直接验证逻辑
新手建议先跳过docker-compose.yml,用纯 Python 方式验证核心链路是否通畅:
# 步骤 1:创建虚拟环境并安装依赖(注意:不要用全局 pip) python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install -r requirements.txt # 步骤 2:初始化数据库(会自动建表、插默认用户 admin/admin) python database.py --init # 步骤 3:启动 Flask 开发服务器 export FLASK_APP=app.py export FLASK_ENV=development flask run --host=0.0.0.0 --port=5000此时访问http://localhost:5000,你会看到登录页(login.png)。用默认账号admin/admin登录后,首页是finance_board.png——一个包含「本月收支趋势图」「Top5 支出类别」「预算完成度环形图」的 Dashboard。这不是前端 mock 数据,所有图表数据均来自database.py的实时 SQL 查询(见app.py中/api/dashboard路由)。
2.3 关键配置项说明:为什么这些参数不能乱改
| 配置项 | 默认值 | 作用 | 修改建议 |
|---|---|---|---|
BUDGET_MONTHLY=8000 | 8000 | 月度总预算阈值(单位:元) | 在.env中修改,需重启服务生效 |
ALERT_THRESHOLD=0.9 | 0.9 | 预算使用率超此值触发邮件提醒(当前仅打印日志) | 若启用邮件,需配SMTP_SERVER等 4 个 SMTP 参数 |
CLASSIFIER_MODEL_PATH | static/models/tfidf_vectorizer.pkl | 账单分类器向量器路径 | 模型文件已固化,勿删;如需重训,运行python bill_classifier.py --train |
UPLOAD_FOLDER | uploads/ | 用户上传账单文件的临时目录 | 必须存在且有写权限,建议mkdir uploads |
特别注意:app.py中所有数据库操作都封装在with db.session.begin():上下文中,确保事务原子性。比如「导入微信账单」操作包含:解析 CSV → 插入bill表 → 更新category_summary视图 → 触发预算检查 —— 任一环节失败,整批回滚。
3. 账单自动化:微信/支付宝 CSV 导入的底层逻辑与定制化改造
3.1 微信账单解析:从原始 CSV 到结构化记录的 7 步映射
微信导出的微信账单.csv是 UTF-8 BOM 编码,字段以英文逗号分隔,但无表头。wechat_bill_processor.py的核心逻辑如下:
def parse_wechat_csv(file_path: str) -> List[Dict]: records = [] with open(file_path, 'r', encoding='utf-8-sig') as f: lines = f.readlines() # 微信 CSV 固定:第 0 行为「微信支付账单明细列表」,第 1 行为空,第 2 行起为数据 for line in lines[2:]: cols = [c.strip('"') for c in line.strip().split(',')] if len(cols) < 12: # 至少需 12 列(含交易时间、金额、类型等) continue # 关键字段映射(微信 CSV 列序固定,不可靠字段用空字符串兜底) record = { 'transaction_time': cols[0], # 交易时间:2024-03-15 14:22:33 'amount': float(cols[3]) if cols[3] else 0.0, # 金额(第 4 列,注意负数为支出) 'type': cols[5].strip(), # 交易类型:「转账」「红包」「商家消费」 'merchant': cols[7].strip() if len(cols) > 7 else '', # 商户名称 'notes': cols[11].strip() if len(cols) > 11 else '', # 备注(常含商品名) } records.append(record) return records这段代码的健壮性体现在三点:
- BOM 自动识别:
encoding='utf-8-sig'确保 Windows 下导出的 CSV 不乱码; - 列序容错:微信 CSV 版本迭代过多次,但「交易时间」「金额」「类型」三列位置从未变动,其他字段缺失时用空字符串填充,避免
IndexError; - 金额符号统一:微信支出为负数(如
-28.50),收入为正数(如+100.00),float()自动转换,后续在database.py中存为DECIMAL(10,2)类型。
3.2 支付宝账单适配:应对「明细账单」与「汇总账单」的双模式
支付宝导出更复杂:用户可能选「明细账单」(含每笔流水)或「汇总账单」(按日汇总)。系统只支持前者,其 CSV 特征为:
- 第 0 行:「支付宝(中国)网络技术有限公司」
- 第 1 行:「账单明细」
- 第 2 行:表头(
交易时间,交易分类,交易对方,商品说明,收/支,金额,收付款方式,...) - 第 3 行起:数据
alipay_bill_processor.py的解析逻辑强制校验表头:
def parse_alipay_csv(file_path: str) -> List[Dict]: with open(file_path, 'r', encoding='utf-8') as f: reader = csv.DictReader(f) # 必须含指定字段,否则抛异常(防止用户误传汇总账单) required_fields = {'交易时间', '收/支', '金额', '交易分类'} if not required_fields.issubset(set(reader.fieldnames)): raise ValueError(f"Alipay CSV missing required fields: {required_fields}") records = [] for row in reader: # 支付宝「收/支」字段值为「支出」或「收入」,需转为标准符号 amount = float(row['金额']) if row['收/支'] == '支出': amount = -abs(amount) records.append({ 'transaction_time': row['交易时间'], 'amount': amount, 'category': row.get('交易分类', '').strip(), 'merchant': row.get('交易对方', '').strip(), 'notes': row.get('商品说明', '').strip(), }) return records注意:支付宝「交易分类」字段(如「餐饮美食」「交通出行」)会被直接映射到系统
bill_types.py中定义的CATEGORY_MAP,若遇到新分类(如「视频会员」),需手动追加映射,否则归入other。
3.3 批量导入实战:用import_wechat_bills.py处理 200+ 笔历史账单
假设你把过去 12 个月的微信账单 CSV 全部下载到./bills/wechat/目录下,执行:
python import_wechat_bills.py -p ./bills/wechat/ -u admin该脚本会:
- 遍历目录下所有
.csv文件; - 对每个文件调用
wechat_bill_processor.py解析; - 将解析结果批量插入数据库(非逐条
INSERT,用session.bulk_insert_mappings()提升性能); - 输出统计:
Processed 142 files, inserted 2187 records, skipped 3 duplicates。
关键参数说明:
-p:指定账单文件夹路径(必填);-u:指定归属用户(用户名,非 ID);--dry-run:模拟运行,不写库,只打印将插入的记录数;--skip-duplicate:根据transaction_time + amount + merchant三元组去重(防重复导入)。
4. 避坑指南:5 个血泪经验换来的「必踩坑」清单
4.1 现象:flask run报错ModuleNotFoundError: No module named 'psycopg2'
原因:requirements.txt中psycopg2-binary是编译型依赖,在某些 Linux 发行版(如 Alpine)或 M1 Mac 上需额外编译工具链;而pip install默认不装编译器。
解决:
- Ubuntu/Debian:
sudo apt-get install libpq-dev python3-dev - CentOS/RHEL:
sudo yum install postgresql-devel python3-devel - macOS(M1):
brew install postgresql,再pip install psycopg2-binary - 或直接改用
pip install "psycopg2-binary>=2.9.0"(二进制包免编译)
4.2 现象:微信账单导入后,所有金额显示为0.00
原因:微信 CSV 导出时选择了「简版」而非「详细版」,导致第 4 列(金额)为空;或文件编码不是 UTF-8 BOM(如 ANSI)。
解决:
- 重新导出微信账单:微信 PC 端 → 左下角「更多」→ 「账单」→ 右上角「...」→ 「导出账单」→ 勾选「详细版」;
- 用 VS Code 打开 CSV,右下角确认编码为
UTF-8 with BOM,若为GBK,点击编码 → 「Reopen with Encoding」→ 选UTF-8 with BOM→ 保存。
4.3 现象:登录后 Dashboard 图表空白,控制台报TypeError: Cannot read property 'data' of undefined
原因:前端finance_board.png对应的dashboard.js依赖 Chart.js v3.x,但requirements.txt中flask未锁版本,某些旧版 Flask 与 Jinja2 冲突导致模板变量未渲染。
解决:
- 在
.env中添加FLASK_DEBUG=True,访问http://localhost:5000/api/dashboard查看返回 JSON 是否为空; - 若返回空,检查
database.py中get_monthly_summary()函数是否因时区问题查不到数据(默认用datetime.now(),应改为datetime.utcnow()); - 强制升级:
pip install flask==2.3.3 jinja2==3.1.3
4.4 现象:make test运行失败,提示pytest: command not found
原因:Makefile中test目标依赖pytest,但requirements.txt未声明pytest为dev依赖,且pip install -r requirements.txt不安装dev组。
解决:
- 手动安装:
pip install pytest pytest-cov; - 或修改
requirements.txt,末尾添加:# dev dependencies pytest>=7.0.0 pytest-cov>=4.0.0
4.5 现象:Docker 启动后http://localhost:5000无法访问,docker logs finance_app显示Connection refused
原因:docker-compose.yml中finance_app服务依赖finance_db,但healthcheck超时(默认 30s),PostgreSQL 容器启动慢于应用容器。
解决:
- 修改
docker-compose.yml中finance_app的depends_on:depends_on: finance_db: condition: service_healthy - 并在
finance_db下添加健康检查:healthcheck: test: ["CMD-SHELL", "pg_isready -U finance -d finance_db"] interval: 30s timeout: 10s retries: 5
5. 分类器调优:不用深度学习,用 30 行代码提升账单自动分类准确率至 92%
5.1 理解当前分类器:规则优先 + TF-IDF 兜底的混合策略
系统没用 BERT 或 Llama 做账单分类,因为:
- 单条账单文本极短(平均 8 个字),大模型 overkill;
- 用户场景高度垂直(餐饮/交通/购物/娱乐),规则覆盖率达 75%;
- TF-IDF + LogisticRegression 在 2000 条标注样本上已达 89% 准确率,训练快、推理快、可解释。
bill_classifier.py的分类流程是:
- 规则匹配层:遍历
bill_types.py中RULES字典(如{'美团': '餐饮', '地铁': '交通'}),用if keyword in notes or keyword in merchant:粗筛; - TF-IDF 层:对未匹配的账单,拼接
merchant + notes作为文本,用预训练TfidfVectorizer向量化; - LR 层:输入向量到
LogisticRegression模型,输出概率最高的类别。
提示:
RULES是可编辑的。比如你常买「得到 App」课程,就在bill_types.py中加'得到' : '教育',下次导入自动归类。
5.2 重训分类器:用你的真实账单数据微调模型
假设你已积累 500 条人工标注账单(格式:id,merchant,notes,category的 CSV),执行:
python bill_classifier.py \ --train \ --data-path ./my_labeled_bills.csv \ --model-output static/models/my_tfidf.pkl \ --vectorizer-output static/models/my_vectorizer.pkl该命令会:
- 读取 CSV,清洗
merchant/notes(去空格、转小写、过滤 emoji); - 用
TfidfVectorizer(max_features=5000, ngram_range=(1,2))提取特征; - 训练
LogisticRegression(C=1.0, max_iter=1000); - 保存模型和向量器到指定路径。
然后修改app.py中CLASSIFIER_MODEL_PATH指向新路径,重启服务即可生效。
5.3 准确率验证:用混淆矩阵定位分类瓶颈
重训后,务必验证效果。运行:
python bill_classifier.py \ --evaluate \ --model-path static/models/my_tfidf.pkl \ --vectorizer-path static/models/my_vectorizer.pkl \ --test-data ./test_bills.csv输出示例:
Classification Report: precision recall f1-score support 餐饮 0.94 0.96 0.95 120 交通 0.89 0.91 0.90 85 购物 0.92 0.88 0.90 110 娱乐 0.85 0.82 0.83 65 education 0.96 0.98 0.97 40 accuracy 0.92 420若「娱乐」类 recall 低(如 0.82),说明模型漏判多——打开test_bills.csv,筛选category==娱乐但预测错的样本,发现它们共性(如都含「KTV」但向量器未收录),就去bill_types.py的RULES中加'KTV': '娱乐'。
5.4 进阶技巧:用find_keyword.png快速定位分类盲区
项目根目录下find_keyword.png是一张交互式热力图(由tools.md中的 Python 脚本生成):横轴为账单文本词频 Top 50,纵轴为各类别,颜色深浅表示该词在该类中的 TF-IDF 权重。
- 操作:用浏览器打开
find_keyword.png,放大查看「购物」类中权重最高的词(如「京东」「淘宝」); - 发现:若「得物」在「购物」类权重为 0,说明训练数据中无「得物」样本;
- 行动:在
my_labeled_bills.csv中补 5 条「得物」账单,重训模型。
从那以后我每次新增商户,都强制走一遍「加 RULE → 导入样本 → 重训 → 验证混淆矩阵」流程,再也没出现过分类漂移。账单分类不是玄学,是可测量、可迭代、可交付的工程活——希望帮到你。
本文还有配套的精品资源,点击获取