🚀

用Python+SQLite搭建个人记账本,告别记账焦虑

纯本地运行,零依赖部署。输入消费记录自动分类,月底一键生成饼图报表。数据全在本地,隐私安全。

👁️ - 次阅读

每个月工资一发就花光?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

要不要试试这个记账本?有问题随时来聊。

这篇文章对你有帮助吗?