十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python本地搭建东京开销记录与数据分析系统

Python本地搭建东京开销记录与数据分析系统 你是不是也刷到过类似标题的视频在日本东京上一天班通勤、午饭、咖啡、零碎支出全部加起来一天到底要花多少钱视频标题里的“26.8.8”可以当作一个基准日的开销编号但真正有价值的问题不是这个数字本身而是这套开销记录能不能被复制、被批量统计、被接口调用作为一个常写本地部署和数据处理的技术博主我第一反应不是羡慕博主的生活而是想把“东京打工人每日开销”做成一套本地可重复运行的数据记录与分析工具从“看别人记录”变成“自己也能统计”。这篇文章就用“在东京上一天班要花多少钱”这个场景带你搭建一个完全本地运行的开销记录与分析小系统。核心内容包括账单数据的结构化设计、公交 IC 卡与餐饮小票的批量导入、汇率换算、每日/每周汇总、图表展示以及后续扩展为本地 Web API 服务的方法。整个过程不依赖云服务数据留在本机适合任何有 Python 基础、想用代码管理个人财务数据的读者。这套方案最大的特点是它不绑定某个博主不绑定“东京”这个城市。换到国内一线城市、换到远程办公、换到自由职业只要改一下科目分类和汇率规则同一套代码就能继续用。这也是我更推荐你自己搭一套的原因。接下来我会直接给出一套可以照着跑的流程包括环境准备、初始化数据库、录入样例数据、生成统计图表、导入批量小票、以及用 Flask 封装成接口。每步都会给出可复制的代码和验证方法。1. 核心能力速览先给一张速览表看完你就知道这套“东京打工开销记录系统”在能力项上大概是什么量级。能力项说明项目类型生活开销记录与数据分析工具非 AI 模型类项目核心功能通勤/餐饮/饮料/杂费记录日报、周报、月报汇总汇率换算图表导出数据存储SQLite 本地数据库同时支持 CSV 导入导出推荐硬件普通办公电脑即可无显卡要求CPU 即可运行显存占用无 GPU 需求不涉及显存占用支持平台Windows / macOS / Linux启动方式Python 脚本启动可选 Flask Web API 模式是否支持 API支持可通过 Flask 暴露“记账/查询/汇总”接口是否支持批量任务支持CSV 批量导入、IC 卡记录批量解析、定时汇总适合场景个人账单管理、通勤成本测算、城市生活成本对比、自媒体开销记录素材整理这套系统解决的核心问题有两个一是把每日零散开支统一成结构化数据二是把“今天花多少”与“这个月花多少”的统计口径固定下来。很多人记账坚持不下去往往不是懒而是数据格式太乱月底没办法做有效分析。它不适合的场景也很明显不适合做复杂的家庭资产配置不适合处理多币种投资组合也不适合代替专业财务软件做发票报销。它更偏向“个人开销数据的收集、清洗、汇总、可视化”这个中间层。2. 适用场景与使用边界2.1 适合谁用第一类计划去东京工作或留学的人。你要评估一个月通勤加吃饭到底要准备多少预算这套工具可以把“交通费、午餐饮料费、偶尔外食、加班后便利店采购”这些项目拆开记录每周自动汇总方便你调整预算。第二类做城市生活成本对比的内容创作者。你可以用同一条数据管道录入东京、上海、北京、深圳的消费数据然后输出统一格式的图表这种“同口径对比”比单纯贴小票更有说服力。第三类想练手 Python 数据处理的普通开发者。这个项目规模不大但覆盖了 SQLite 建表、CSV 读写、日期处理、matplotlib 图表、Flask API、批量导入等常见工程点很适合作为入门练习。2.2 使用边界与合规提醒这里必须多说几句。虽然记录开销本身没有风险但涉及个人消费数据、小票照片、IC 卡刷卡记录时要注意隐私和数据合规。小票照片可能包含门店地址、会员卡号、支付方式、甚至部分银行卡尾号做 OCR 识别后建议及时删除原图。不要把包含个人身份信息的账单上传到不可控的第三方服务。如果后续把 Flask 服务暴露到局域网或公网先加一个最简单的 Token 校验避免任何人直接读写你的记账接口。如果你后续做“日本生活开销”相关内容涉及便利店品牌、车站名、门店价格时请以实地记录和公开信息为准不要编造数据。内容创作场景下如果要引用他人视频中的开销数据需要先获得授权不能直接挪用他人的真实记账明细。数据合规不是口号是会直接影响你是否能长期使用这个工具的关键。上面这些约束不难做到但必须在项目一开始就设计进去。3. 环境准备与前置条件这是一套轻量级 Python 项目对环境的要求很低。Windows 10/11、macOS、主流 Linux 发行版都可以运行。下面是我的推荐环境清单不同版本差异不大不需要严格照抄。3.1 推荐环境依赖项说明Python建议 3.10 或更高版本操作系统Windows 10/11, macOS 12, Ubuntu 20.04数据库SQLitePython 内置无需额外安装Python 依赖库pandas, matplotlib, flask, requestsOCR 可选paddleocr 或 tesseract仅在批量识别小票时使用开发工具VS Code 或任何代码编辑器3.2 基础依赖安装先创建一个项目目录并初始化虚拟环境。这里以 Windows 命令行为例macOS/Linux 下把venv\Scripts\activate替换为venv/bin/activate即可。mkdir tokyo-cost-tracker cd tokyo-cost-tracker python -m venv venv venv\Scripts\activate激活虚拟环境后安装核心依赖。pip install pandas matplotlib flask requests如果后续需要小票 OCR再单独安装 OCR 库。这里给出 tesseract 的 Python 封装安装方式但需要注意的是实际识别效果与图片清晰度、日语/中文语言包是否安装都有关系。pip install pytesseractOCR 部分需要额外安装 Tesseract 本体程序不同系统安装方式不同建议先跳过这一步文章后面会说明纯手工输入和 OCR 批量导入两条路径的区别。3.3 验证环境安装完成后运行下面这段代码确认 pandas 和 matplotlib 可以正常导入。import pandas as pd import matplotlib.pyplot as plt print(pandas version:, pd.__version__) print(matplotlib version:, plt.matplotlib.__version__)如果输出两个版本号说明环境已经准备好。4. 安装部署与启动方式这个项目不依赖远程模型也不存在模型文件下载所以“部署”的本质是“初始化本地数据库并写好核心模块”。我把它拆成一个主程序加两个模块数据库模块、统计模块、Web 服务模块。下面会逐步给出代码。4.1 初始化数据库创建db.py用于初始化 SQLite 数据库和文件表结构。import sqlite3 from datetime import date DB_PATH cost.db def init_db(): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS daily_cost ( id INTEGER PRIMARY KEY AUTOINCREMENT, cost_date TEXT NOT NULL, category TEXT NOT NULL, item_name TEXT, amount_jpy REAL NOT NULL, currency TEXT DEFAULT JPY, note TEXT ) ) conn.commit() conn.close() def add_record(cost_date, category, item_name, amount_jpy, note): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.execute( INSERT INTO daily_cost (cost_date, category, item_name, amount_jpy, note) VALUES (?, ?, ?, ?, ?), (cost_date, category, item_name, amount_jpy, note), ) conn.commit() conn.close()这个表结构很简单一条记录对应一个消费项目字段包括日期、分类、名称、日元金额、备注。对于个人记账来说足够用了。4.2 启动方式命令行交互录入创建main.py提供最简单的命令行录入和汇总功能。from db import init_db, add_record def main(): init_db() print(东京打工人开销记录系统) print(输入格式日期 分类 金额 名称) print(示例2026-08-08 transport 780 Suica充值) print(输入 q 退出) while True: line input( ).strip() if line.lower() q: break parts line.split() if len(parts) 3: print(格式错误请重新输入) continue cost_date, category, amount_jpy parts[0], parts[1], float(parts[2]) item_name .join(parts[3:]) add_record(cost_date, category, item_name, amount_jpy) if __name__ __main__: main()运行方式python main.py启动后你会看到交互提示输入2026-08-08 transport 780 Suica充值就会写入一条通勤记录。这里的分类完全自定义常见分类我建议使用transport、lunch、drink、snack、other。4.3 一键启动批量初始化脚本为了减少重复输入我再给一个一键初始化脚本setup.py它会在第一次运行时创建数据库、写入演示数据、并输出汇总结果。from db import init_db, add_record from stats import daily_summary def setup_demo(): init_db() demo_records [ (2026-08-08, transport, Suica充值, 780, 通勤电车), (2026-08-08, lunch, 便利店便当, 680, 午餐), (2026-08-08, drink, 罐装咖啡, 130, 咖啡), (2026-08-09, transport, Suica充值, 780, 通勤电车), (2026-08-09, lunch, 定食, 980, 午餐), (2026-08-09, snack, 零食, 200, 下午零食), ] for r in demo_records: add_record(*r) print(daily_summary()) if __name__ __main__: setup_demo()这个脚本可以让你在 10 秒内看到一套可用的演示数据流后面换成真实记录时只需把demo_records换成从 CSV 读取即可。5. 功能测试与效果验证部署完成之后不能只看“能启动”还要验证数据记录、汇总统计、汇率换算和图表输出是否正常。下面按功能维度写测试用例。5.1 测试目标记录功能能够写入正确的一条消费记录。汇总功能能够按日、按周、按分类汇总金额。汇率换算能把日元金额换算成人民币或其他货币。图表功能能输出每日开销折线图或分类占比图。5.2 汇总统计模块创建stats.py实现按日汇总和按分类汇总。import sqlite3 from db import DB_PATH def _query(sql, params()): conn sqlite3.connect(DB_PATH) df pd.read_sql_query(sql, conn, paramsparams) conn.close() return df def daily_summary(): df _query(SELECT cost_date, SUM(amount_jpy) AS total_jpy FROM daily_cost GROUP BY cost_date ORDER BY cost_date) return df def category_summary(start_date, end_date): df _query( SELECT category, SUM(amount_jpy) AS total_jpy FROM daily_cost WHERE cost_date BETWEEN ? AND ? GROUP BY category ORDER BY total_jpy DESC, (start_date, end_date), ) return df这里依赖 pandas 的read_sql_query需要先确保 stats.py 里导入了 pandas。import pandas as pd5.3 汇率换算测试海外开销记录最容易踩的坑是汇率。日元汇率每天都在变所以我建议把汇率单独做成一个函数不写死在数据表里。下面是一个通用方案def jpy_to_cny(amount_jpy, rate): return amount_jpy * rate这里的rate是“1 日元等于多少人民币”。实际使用时你可以手动从银行汇率页面读取也可以调用汇率接口。但需要注意免费汇率接口随时可能失效稳妥做法是在代码里配置一个默认汇率再留一个参数覆盖入口。# 示例默认汇率实际使用时以当天汇率为准 DEFAULT_RATE 0.048 def jpy_to_cny(amount_jpy, rateDEFAULT_RATE): return round(amount_jpy * rate, 2)测试时可以直接打印print(jpy_to_cny(2590)) # 输入总日元金额输出人民币估算金额从材料看标题里的 26.8.8 可以看作一个记录基准日的编号具体金额是否准确取决于你采用什么汇率、是否包含通勤月票均摊这些口径必须提前定义好否则后续统计会不一致。5.4 测试用例表格功能测试输入预期输出判断是否成功记录写入2026-08-08 transport 780 Suica充值数据库新增一条 transport 记录再次查询能看到该记录日汇总查询 2026-08-08总金额 780 680 130 1590 日元金额与手工计算一致分类汇总查询 2026-08-08 到 2026-08-09transport 合计 1560 日元两天的通勤费用正确合并汇率换算1590 日元汇率 0.048约 76.32 人民币保留两位小数输出图表生成传入 7 日每日总金额生成 PNG 图片图片能正常打开5.5 图表输出创建chart.py生成每日开销折线图和分类占比饼图。这里用 matplotlib 的中文字体不同系统需要调整Windows 下通常可以直接运行macOS 下可能需要额外配置字体。import matplotlib.pyplot as plt import pandas as pd from stats import daily_summary, category_summary def plot_daily(start_date, end_date): df daily_summary() df[cost_date] pd.to_datetime(df[cost_date]) mask (df[cost_date] start_date) (df[cost_date] end_date) df df[mask] plt.figure(figsize(10, 4)) plt.plot(df[cost_date], df[total_jpy], markero) plt.title(Daily Cost (JPY)) plt.xlabel(Date) plt.ylabel(Amount) plt.grid(True) plt.tight_layout() plt.savefig(daily_cost.png) plt.close() def plot_category(start_date, end_date): df category_summary(start_date, end_date) plt.figure(figsize(6, 6)) plt.pie(df[total_jpy], labelsdf[category], autopct%.1f%%) plt.title(Category Ratio) plt.savefig(category_ratio.png) plt.close()运行后会在当前目录生成两个 PNG 文件。这一步是“效果验证”最直观的部分看到图片就说明整个数据链已经通了。6. 接口 API 与批量任务命令行工具适合自己用但如果你想把它接到其他工具里或者做批量导入就需要一个接口层。下面用 Flask 暴露三个最小接口写入记录、查询汇总、批量导入。6.1 启动 Flask 服务创建app.py。import sqlite3 import pandas as pd from flask import Flask, request, jsonify from db import init_db, add_record, DB_PATH app Flask(__name__) app.route(/api/add, methods[POST]) def api_add(): data request.get_json(forceTrue) add_record( cost_datedata[cost_date], categorydata[category], item_namedata.get(item_name, ), amount_jpyfloat(data[amount_jpy]), notedata.get(note, ), ) return jsonify({status: ok}) app.route(/api/summary, methods[GET]) def api_summary(): conn sqlite3.connect(DB_PATH) df pd.read_sql_query(SELECT cost_date, SUM(amount_jpy) AS total_jpy FROM daily_cost GROUP BY cost_date ORDER BY cost_date, conn) conn.close() return jsonify(df.to_dict(orientrecords)) app.route(/api/batch_import, methods[POST]) def api_batch_import(): data request.get_json(forceTrue) records data[records] for r in records: add_record( cost_dater[cost_date], categoryr[category], item_namer.get(item_name, ), amount_jpyfloat(r[amount_jpy]), noter.get(note, ), ) return jsonify({status: ok, count: len(records)}) if __name__ __main__: init_db() app.run(host127.0.0.1, port5000)启动方式python app.py启动后浏览器访问http://127.0.0.1:5000/api/summary就能看到当前所有日期的汇总。6.2 curl 调用示例新增一条记录curl -X POST http://127.0.0.1:5000/api/add \ -H Content-Type: application/json \ -d {\cost_date\:\2026-08-10\,\category\:\transport\,\item_name\:\Suica充值\,\amount_jpy\:780}查看汇总curl http://127.0.0.1:5000/api/summary批量导入curl -X POST http://127.0.0.1:5000/api/batch_import \ -H Content-Type: application/json \ -d {\records\:[{\cost_date\:\2026-08-10\,\category\:\lunch\,\amount_jpy\:800},{\cost_date\:\2026-08-10\,\category\:\drink\,\amount_jpy\:150}]}能看到返回 JSON 的status字段为ok说明接口已经跑通。后面你要接到微信群机器人、桌面小组件或者自己的 Vue 页面只需要按这个 JSON 结构对接。6.3 批量任务设计批量任务的本质是“一次处理多条记录并保证出错时可定位、可重试”。实际使用中我最推荐的做法是维护一个records.csv文件然后用批处理脚本统一导入。cost_date,category,item_name,amount_jpy,note 2026-08-08,transport,Suica充值,780,通勤电车 2026-08-08,lunch,便利店便当,680,午餐 2026-08-08,drink,罐装咖啡,130,咖啡导入脚本batch_import.pyimport csv from db import init_db, add_record def import_csv(path): with open(path, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: add_record( cost_daterow[cost_date], categoryrow[category], item_namerow.get(item_name, ), amount_jpyfloat(row[amount_jpy]), noterow.get(note, ), ) print(import done) if __name__ __main__: init_db() import_csv(records.csv)批量任务建议加日志记录每一行的导入结果这样一旦某条数据的格式有问题你可以很快定位而不是全部失败后只能重来。7. 资源占用与性能观察这套系统不涉及 GPU 和显存但它依然有“性能”问题只是观察维度不一样主要看三块数据量增长、OCR 耗时、图表渲染耗时。7.1 数据量级一条消费记录在 SQLite 里大概只占几十字节。即使你每天记录 20 条一年也就 7000 多条放在 SQLite 里完全没有压力。哪怕连续记录十年数据量也不会超过几十万条级别。对 pandas 来说这个规模属于秒出结果的范围。7.2 OCR 批量识别耗时如果你使用小票 OCR这一步是整个流程中最耗时的。一张手机拍摄的小票照片从发送到 OCR 服务到返回文字通常需要几秒到几十秒取决于网络状况和图片大小。批量识别 100 张小票可能需要十几分钟所以必须做成离线批量任务而不是在用户交互里同步等待。建议做法先把小票图片集中放在receipts/inbox/目录跑一个扫描脚本识别完成后把结果写入pending_import.csv人工确认后再导入数据库。7.3 如何观察性能你可以用 Python 的time模块观察每个函数的耗时但更重要的是记录任务日志。这里给一个通用日志配置示例import logging logging.basicConfig( filenametracker.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, ) def log_import(record_id, status): logging.info(frecord {record_id}: {status})这样每次批量导入都能清楚看到哪条成功、哪条失败、耗时差距。对于个人项目这就足够满足了。8. 常见问题与排查方法下面整理了一套我在设计这类工具时会提前规避的问题清单你也可以把它当作验收清单来用。问题现象可能原因排查方式解决方案启动后提示找不到模块pandas虚拟环境未激活或依赖未安装查看终端是否进入 venv激活虚拟环境执行pip install pandas matplotlib flask requests中文内容乱码CSV 编码不是 UTF-8用编辑器查看文件编码保存为 UTF-8 编码读取时指定encodingutf-8图表中文显示为方框系统缺少中文字体检查 matplotlib 字体列表安装中文字体或使用plt.rcParams[font.sans-serif]指定字体Flask 接口无法访问服务没有启动或端口被占用查看终端日志检查端口换端口启动比如app.run(port5001)端口 5000 被占用其他服务占用了 5000在终端运行端口查询命令修改 app.py 中的port批量导入时部分数据没有写入CSV 中日期格式或金额格式有误打印每行解析结果查看跟踪日志修正 CSV 中对应行重新导入OCR 识别结果不稳定图片模糊、反光、文字倾斜检查原图质量观察识别文本重新拍摄或人工补录错漏字段日元金额换算不准确汇率设置过期检查当天汇率手动更新默认汇率参数记录重复手动录入与批量导入重复执行查询数据库总行数增加去重逻辑比如用日期分类金额做去重约束从实际运维角度看最常发生的不是数据库问题而是“导入了几次、重复了很多遍”。解决思路很简单在daily_cost表上增加一个唯一索引字段组合是cost_date item_name amount_jpy再配合导入前查询基本能避免大部分重复。CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_record ON daily_cost (cost_date, item_name, amount_jpy);9. 最佳实践与使用建议这部分是真正提升使用体验的关键不建议跳过。9.1 分类口径先统一开始记账前先确定分类列表。我建议控制在 5 到 7 个以内分类太多会导致每次录入都在犹豫。以东京通勤场景为例transport通勤交通lunch午餐drink咖啡、饮料snack零食other临时支出等积累一个月数据后再根据真实情况拆分比如把other再拆成medical、entertainment等。9.2 数据和代码分离建议保持以下目录结构tokyo-cost-tracker/ ├── data/ │ ├── cost.db │ └── records.csv ├── receipts/ │ ├── inbox/ │ └── done/ ├── outputs/ │ ├── daily_cost.png │ └── category_ratio.png ├── src/ │ ├── db.py │ ├── stats.py │ ├── chart.py │ └── app.py └── requirements.txt代码归代码数据归数据图片输出放到outputs。这样即便你后续换电脑、迁移项目也不会误删原始账单。9.3 用日志代替“感觉自己记过”不要依赖记忆判断是否重复导入。每次批量导入前先查询数据库记录数导入后再次查询用数字确认。再加一条导入日志记录时间、导入文件名、成功条数、失败条数后续排错会非常省力。9.4 接口服务限制访问范围默认情况下 Flask 监听的地址是127.0.0.1只能本机访问。如果你要测试局域网访问可以改成0.0.0.0但必须确认你的网络环境可信并建议加上 Token 校验否则任何人都可以往你的数据库里写数据。9.5 涉及人脸、小票、账单素材时的合规处理如果你要把这种开销记录做成视频或图文内容建议做两层处理第一层在素材中打码门店名、支付方式、会员卡号等个人信息第二层如果引用其他博主的开销数据需要获得授权。不要为了流量去搬运他人的真实账单明细。对于个人使用还有一个容易被忽略的点不要把数据库原文件直接上传到网盘。SQLite 文件里存的是你每天什么时间在哪花了多少钱这类信息属于敏感个人数据建议加密压缩后再备份。10. 总结与下一步这篇文章用一个很容易被当生活流水账的主题拆解成一套可运行、可复用、可扩展的本地数据记录系统。最值得试的三个点第一用 SQLite pandas 把零散的东京开销整理成结构化数据第二用 Flask 把记账能力封装成接口之后可以接任何前端工具第三通过 CSV 批量任务一次导入一周甚至一个月的账单省去手工逐条录入。建议你先跑通setup.py的演示数据再导入自己近一周的真实开销重点看日报和分类占比图是否符合直觉。最容易踩的坑有两个一是汇率写了固定值却不记得更新二是批量导入重复执行导致数据翻了倍。前者建议留一个配置项后者建议加唯一索引和日志。如果你愿意继续扩展下一步可以做三件事把 OCR 小票识别接入批量导入流程实现“拍小票自动记账”把 Flask 接口接入手机快捷指令或桌面小工具再加一个简单的环比统计对比这个月和上个月在“通勤午餐”上的支出变化。这套代码跑顺之后对你评估东京生活成本、做城市消费对比都会比看别人视频更扎实。
返回列表