纯本地运行,零依赖部署。输入消费记录自动分类,月底一键生成饼图报表。数据全在本地,隐私安全。
每个月工资一发就花光?Excel 记账坚持不了三天就放弃?
试试这个方案。一个纯 Python + SQLite 的本地记账本,不用联网,不用注册账号,数据存在自己电脑里。
项目背景
市面上的记账 App 要么要登录,要么要付费,要么满屏广告。
核心需求其实很简单:记一笔账,看钱花哪了。
这个项目实现三个功能:
- 快速录入消费(分类、金额、备注)
- 按月份查看账单汇总
- 自动生成饼图,看看钱都去哪了
全部代码不到 200 行,跑在任意有 Python 3 的环境上。
技术选型
| 组件 | 选择 | 理由 |
|---|---|---|
| 数据库 | SQLite3 | 内置 Python,零配置 |
| 图表 | matplotlib | 生成饼图和柱状图 |
| CLI 交互 | argparse | 命令行参数解析 |
| 日期处理 | datetime | 内置模块,够用 |
实现步骤
第一步:数据库初始化
# bookkeeper.py
import sqlite3
import os
from datetime import datetime
DB_PATH = os.path.join(os.path.dirname(__file__), "expenses.db")
def init_db():
"""创建数据表"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT NOT NULL,
amount REAL NOT NULL,
note TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
# 常用分类
categories = [
"餐饮", "交通", "购物", "娱乐", "住房",
"医疗", "教育", "通讯", "其他"
]
cursor.execute("""
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL
)
""")
for cat in categories:
cursor.execute(
"INSERT OR IGNORE INTO categories (name) VALUES (?)",
(cat,)
)
conn.commit()
conn.close()
print("✅ 数据库初始化完成")
if __name__ == "__main__":
init_db()
运行一次即可:python bookkeeper.py
第二步:添加消费记录
def add_expense(category, amount, note=""):
"""添加一条消费记录"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# 验证分类是否存在
cursor.execute("SELECT 1 FROM categories WHERE name = ?", (category,))
if not cursor.fetchone():
print(f"❌ 未知分类: {category}")
print("可用分类: 餐饮, 交通, 购物, 娱乐, 住房, 医疗, 教育, 通讯, 其他")
conn.close()
return
cursor.execute(
"INSERT INTO expenses (category, amount, note) VALUES (?, ?, ?)",
(category, amount, note)
)
conn.commit()
conn.close()
print(f"✅ 已记录: {category} ¥{amount:.2f} {note}")
使用示例:
python bookkeeper.py add "餐饮" 35 "午餐"
python bookkeeper.py add "交通" 6 "地铁"
python bookkeeper.py add "购物" 199 "书籍"
第三步:查询月度账单
def monthly_report(year, month):
"""生成某月的消费报表"""
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
start = f"{year}-{month:02d}-01"
if month == 12:
end = f"{year + 1}-01-01"
else:
end = f"{year}-{month + 1:02d}-01"
cursor.execute("""
SELECT category, SUM(amount), COUNT(*)
FROM expenses
WHERE created_at >= ? AND created_at < ?
GROUP BY category
ORDER BY SUM(amount) DESC
""", (start, end))
rows = cursor.fetchall()
conn.close()
if not rows:
print(f"\n📭 {year}年{month}月暂无记录")
return
total = sum(r[1] for r in rows)
print(f"\n{'='*40}")
print(f" {year}年{month}月 消费报表")
print(f"{'='*40}")
for cat, amt, cnt in rows:
pct = (amt / total * 100) if total > 0 else 0
bar = "█" * int(pct / 2)
print(f" {cat:<6} ¥{amt:>8.2f} {cnt:>2}笔 {pct:>5.1f}% {bar}")
print(f"{'-'*40}")
print(f" {'合计':<6} ¥{total:>8.2f}")
print(f"{'='*40}\n")
第四步:生成饼图
def generate_chart(year, month):
"""生成月度消费饼图"""
import matplotlib
matplotlib.use("Agg") # 无头模式,不弹出窗口
import matplotlib.pyplot as plt
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
start = f"{year}-{month:02d}-01"
if month == 12:
end = f"{year + 1}-01-01"
else:
end = f"{year}-{month + 1:02d}-01"
cursor.execute("""
SELECT category, SUM(amount)
FROM expenses
WHERE created_at >= ? AND created_at < ?
GROUP BY category
ORDER BY SUM(amount) DESC
""", (start, end))
rows = cursor.fetchall()
conn.close()
if not rows:
print(f"📭 {year}年{month}月无数据,无法生成图表")
return
categories = [r[0] for r in rows]
amounts = [r[1] for r in rows]
# 颜色映射
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE']
fig, ax = plt.subplots(figsize=(8, 6))
wedges, texts, autotexts = ax.pie(
amounts,
labels=categories,
autopct='%1.1f%%',
colors=colors[:len(categories)],
startangle=90,
textprops={'fontsize': 12}
)
ax.set_title(f'{year}年{month}月消费分布', fontsize=16, pad=20)
# 保存图表
output_path = os.path.join(os.path.dirname(__file__), f"chart_{year}_{month:02d}.png")
plt.savefig(output_path, dpi=150, bbox_inches='tight')
print(f"✅ 饼图已生成: {output_path}")
第五步:整合成完整脚本
将上面所有函数整合到一个脚本中,并添加命令行入口:
import argparse
def main():
parser = argparse.ArgumentParser(description="个人记账本")
subparsers = parser.add_subparsers(dest="command", help="可用命令")
# init 命令
subparsers.add_parser("init", help="初始化数据库")
# add 命令
add_parser = subparsers.add_parser("add", help="添加消费记录")
add_parser.add_argument("category", help="分类")
add_parser.add_argument("amount", type=float, help="金额")
add_parser.add_argument("note", nargs="?", default="", help="备注")
# report 命令
report_parser = subparsers.add_parser("report", help="查看月度报表")
report_parser.add_argument("year", type=int, help="年份")
report_parser.add_argument("month", type=int, help="月份")
# chart 命令
chart_parser = subparsers.add_parser("chart", help="生成饼图")
chart_parser.add_argument("year", type=int, help="年份")
chart_parser.add_argument("month", type=int, help="月份")
args = parser.parse_args()
if args.command == "init":
init_db()
elif args.command == "add":
add_expense(args.category, args.amount, args.note)
elif args.command == "report":
monthly_report(args.year, args.month)
elif args.command == "chart":
generate_chart(args.year, args.month)
else:
parser.print_help()
if __name__ == "__main__":
main()
运行效果
初始化数据库
$ python bookkeeper.py init
✅ 数据库初始化完成
添加消费记录
$ python bookkeeper.py add "餐饮" 35 "午餐"
✅ 已记录: 餐饮 ¥35.00 午餐
$ python bookkeeper.py add "交通" 6 "地铁"
✅ 已记录: 交通 ¥6.00 地铁
$ python bookkeeper.py add "购物" 199 "书籍"
✅ 已记录: 购物 ¥199.00 书籍
查看月度报表
$ python bookkeeper.py report 2026 8
========================================
2026年8月 消费报表
========================================
购物 ¥ 199.00 1笔 78.7% ████████████████████
餐饮 ¥ 35.00 1笔 13.8% █████
交通 ¥ 6.00 1笔 2.4% █
----------------------------------------
合计 ¥ 240.00
========================================
生成饼图
$ python bookkeeper.py chart 2026 8
✅ 饼图已生成: /path/to/chart_2026_08.png
生成的饼图如下:

优化方向
这个记账本已经能跑起来,但可以继续完善:
- 自动分类:接入 AI 模型,输入备注自动识别分类
- 数据同步:添加云端备份,手机电脑数据互通
- 预算提醒:设置月度预算,超支时推送通知
- 移动端:用 Streamlit 或 Gradio 搭个网页界面
- 导出报表:支持导出 Excel,方便做更复杂的分析
- 定期清理:添加数据归档功能,只保留最近12个月
部署建议
把脚本放到 VPS 上,配个 cron 定时任务,月底自动发邮件通知你本月消费情况:
# 每天凌晨2点生成上月报表
0 2 1 * * cd ~/bookkeeper && python bookkeeper.py report $(date -d "last month" +%Y) $(date -d "last month" +%m) >> ~/bookkeeper/log.txt 2>&1
要不要试试这个记账本?有问题随时来聊。
这篇文章对你有帮助吗?