基于 Flask 的 Python 个人记账本 Web 应用实现
介绍使用 Python Flask 框架开发个人记账本 Web 应用的完整过程。项目包含支出记录增删改查、分类管理、数据可视化(Matplotlib 生成饼图和柱状图)、Excel 导出及响应式界面设计(Bootstrap)。后端采用 JSON 存储,支持通过 PyInstaller 打包为单文件 EXE。适合 Python Web 初学者学习前后端交互与数据持久化。

介绍使用 Python Flask 框架开发个人记账本 Web 应用的完整过程。项目包含支出记录增删改查、分类管理、数据可视化(Matplotlib 生成饼图和柱状图)、Excel 导出及响应式界面设计(Bootstrap)。后端采用 JSON 存储,支持通过 PyInstaller 打包为单文件 EXE。适合 Python Web 初学者学习前后端交互与数据持久化。

在现代生活中,合理的财务管理和支出记录对每个人都非常重要。本文介绍一个基于 Python Flask 框架开发的个人记账本 Web 应用,支持支出记录、数据统计、可视化图表和数据导出等功能。
personal-account-book/
├── app.py # 主程序文件
├── account_data.json # 数据存储文件
└── build_single.txt # 打包脚本(后缀改为 bat)
class Expense:
"""支出记录类"""
def __init__(self, amount, category, date, note=''):
self.amount = float(amount)
self.category = category
self.date = date
self.note = note
def to_dict(self):
return {
'amount': self.amount,
'category': self.category,
'date': self.date.strftime('%Y-%m-%d'),
'note': self.note
}
class AccountBook:
"""账本管理类"""
def __init__(self):
self.expenses = []
self.filename = "account_data.json"
self.categories = ["餐饮", "交通", "购物", "娱乐", "学习", "医疗", "住房", "其他"]
self.load_data()
def add_expense(self, amount, category, date, note=''):
"""添加支出"""
try:
expense = Expense(amount, category, date, note)
self.expenses.append(expense)
success = self.save_data()
return success
except Exception as e:
print(f"添加失败:{e}")
return False
@app.route('/api/expenses', methods=['POST'])
def add_expense():
"""添加支出记录"""
data = request.json
amount = data.get('amount')
category = data.get('category')
date_str = data.get('date')
note = data.get('note', '')
if not all([amount, category, date_str]):
return jsonify({'success': False, 'message': '请填写完整信息'})
try:
amount_val = float(amount)
if amount_val <= 0:
return jsonify({'success': False, 'message': '金额必须大于 0'})
date = datetime.strptime(date_str, '%Y-%m-%d')
except (ValueError, TypeError) as e:
return jsonify({'success': False, 'message': f'数据格式错误:{e}'})
if account_book.add_expense(amount_val, category, date, note):
return jsonify({'success': True, 'message': '添加成功'})
return jsonify({'success': False, 'message': '添加失败'})
@app.route('/api/chart')
def get_chart():
"""生成图表"""
stats = account_book.get_monthly_statistics()
if not stats['category_totals']:
return jsonify({'success': False, 'message': '没有数据'})
try:
plt.figure(figsize=(10, 8))
categories = list(stats['category_totals'].keys())
amounts = list(stats['category_totals'].values())
# 饼图
plt.subplot(1, 2, 1)
plt.pie(amounts, labels=categories, autopct='%1.1f%%', startangle=90)
plt.title('本月支出分类比例')
# 柱状图
plt.subplot(1, 2, 2)
colors = ['#e74c3c', '#3498db', '#9b59b6', '#f39c12', '#2ecc71', '#e67e22', '#34495e', '#95a5a6']
plt.bar(categories, amounts, color=colors[:len(categories)])
plt.title('各分类支出金额')
plt.xticks(rotation=45)
plt.tight_layout()
img = io.BytesIO()
plt.savefig(img, format='png', bbox_inches='tight', dpi=100)
img.seek(0)
chart_url = base64.b64encode(img.getvalue()).decode()
plt.close()
jsonify({: , : chart_url})
Exception e:
jsonify({: , : })
使用 Bootstrap 5 实现响应式设计,确保在手机、平板、电脑上都有良好的显示效果。
通过 JavaScript 实现前后端交互,无需刷新页面即可完成数据操作:
// 添加支出记录
async function addExpense() {
const amount = document.getElementById('amount').value;
const category = document.getElementById('category').value;
const date = document.getElementById('date').value;
const note = document.getElementById('note').value;
if (!amount || !category || !date) {
showAlert('请填写完整信息', 'warning');
return;
}
try {
const response = await fetch('/api/expenses', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ amount, category, date, note })
});
const result = await response.json();
if (result.success) {
showAlert(result.message, 'success');
.(). = ;
.(). = ;
();
}
} (error) {
( + error, );
}
}
pip install flask pandas matplotlib
python app.py
http://127.0.0.1:5000这个项目还有很大的扩展空间:
通过这个项目,我们不仅实现了一个实用的个人记账工具,还展示了如何使用 Flask 框架快速开发 Web 应用。项目涵盖了前后端开发、数据可视化、文件操作等多个重要知识点,非常适合 Python Web 开发的初学者学习和参考。
import os
import json
import base64
import io
import sys
from datetime import datetime, timedelta
from flask import Flask, render_template_string, request, jsonify, send_file
import pandas as pd
import matplotlib
# 设置系统编码
if sys.stdout.encoding != 'UTF-8':
try:
sys.stdout.reconfigure(encoding='utf-8')
except AttributeError:
pass
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import font_manager
# 设置中文字体支持
def setup_chinese_font():
"""设置中文字体支持"""
try:
# 尝试使用系统中常见的中文字体
font_names = [
'Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'STKaiti', 'STSong',
'LiHei Pro', 'WenQuanYi Micro Hei', 'DejaVu Sans'
]
for font_name in font_names:
if font_manager.findfont(font_name):
plt.rcParams['font.family'] = font_name
plt.rcParams['axes.unicode_minus'] = False
print(f"使用字体:")
plt.rcParams[] = [, ]
plt.rcParams[] =
()
Exception e:
()
setup_chinese_font()
HTML_TEMPLATE =
:
():
.amount = (amount)
.category = category
.date = date
.note = note
():
cls(
data[],
data[],
datetime.strptime(data[], ),
data.get(, )
)
():
{
: .amount,
: .category,
: .date.strftime(),
: .note
}
:
():
.expenses = []
.filename =
.categories = [, , , , , , , ]
.load_data()
():
os.path.exists(.filename):
:
(.filename, , encoding=) f:
data = json.load(f)
.expenses = [Expense.from_dict(item) item data]
()
Exception e:
()
():
:
data = [expense.to_dict() expense .expenses]
(.filename, , encoding=) f:
json.dump(data, f, ensure_ascii=, indent=)
()
Exception e:
()
():
:
expense = Expense(amount, category, date, note)
.expenses.append(expense)
success = .save_data()
success
Exception e:
()
():
<= index < (.expenses):
.expenses.pop(index)
success = .save_data()
success
():
year month:
now = datetime.now()
year, month = now.year, now.month
monthly_expenses = [
exp exp .expenses
exp.date.year == year exp.date.month == month
]
category_totals = {}
exp monthly_expenses:
category_totals[exp.category] = category_totals.get(exp.category, ) + exp.amount
{
: (exp.amount exp monthly_expenses),
: category_totals,
: (monthly_expenses)
}
app = Flask(__name__)
account_book = AccountBook()
():
render_template_string(HTML_TEMPLATE)
():
sorted_expenses = (account_book.expenses, key= x: x.date, reverse=)
jsonify([expense.to_dict() expense sorted_expenses])
():
data = request.json
amount = data.get()
category = data.get()
date_str = data.get()
note = data.get(, )
([amount, category, date_str]):
jsonify({: , : })
:
amount_val = (amount)
amount_val <= :
jsonify({: , : })
date = datetime.strptime(date_str, )
(ValueError, TypeError) e:
jsonify({: , : })
account_book.add_expense(amount_val, category, date, note):
jsonify({: , : })
jsonify({: , : })
():
account_book.delete_expense(index):
jsonify({: , : })
jsonify({: , : })
():
jsonify(account_book.get_monthly_statistics())
():
stats = account_book.get_monthly_statistics()
stats[]:
jsonify({: , : })
:
plt.figure(figsize=(, ))
categories = (stats[].keys())
amounts = (stats[].values())
plt.subplot(, , )
plt.pie(amounts, labels=categories, autopct=, startangle=)
plt.title()
plt.subplot(, , )
colors = [, , , , , , , ]
plt.bar(categories, amounts, color=colors[:(categories)])
plt.title()
plt.xticks(rotation=)
plt.tight_layout()
img = io.BytesIO()
plt.savefig(img, =, bbox_inches=, dpi=)
img.seek()
chart_url = base64.b64encode(img.getvalue()).decode()
plt.close()
jsonify({: , : chart_url})
Exception e:
()
jsonify({: , : })
():
account_book.expenses:
jsonify({: , : })
:
data = [expense.to_dict() expense account_book.expenses]
df = pd.DataFrame(data)
filename =
df.to_excel(filename, index=, encoding=)
send_file(filename, as_attachment=)
Exception e:
jsonify({: , : })
():
( * )
()
( * )
()
()
()
( * )
:
webbrowser
webbrowser.()
Exception e:
()
app.run(debug=, host=, port=)
__name__ == :
main()
@echo off
chcp 65001
echo 正在打包单文件 Web 记账本...
pyinstaller --onefile --noconsole --name="Web 记账本" ^
--add-data "account_data.json;." ^
--hidden-import=matplotlib.backends.backend_agg ^
--hidden-import=flask ^
--hidden-import=pandas ^
web_account_app.py
echo.
if %errorlevel% == 0 (
echo 打包成功!
echo 生成的 exe 文件在 dist 文件夹中
echo 使用方法:双击 "Web 记账本.exe",自动打开浏览器
) else (
echo 打包失败!
)
pause
注意修改文件后缀为 .bat,双击运行即可。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online