AnyPS5实战:用SQLite构建本地游戏库管理与统计工具

发布时间:2026/10/12 1:39:21
AnyPS5实战:用SQLite构建本地游戏库管理与统计工具 1. 游戏库从第三十款开始失控我为什么要写AnyPS5说实话我的PS5游戏库大概从第三十款开始就彻底失控了。当时我对着主机里的游戏列表想找某款回合制RPG想了半天没想明白它到底是实体盘还是数字版、当时多少钱入的、还差几个奖杯能白金。群里朋友建议我拉个表格但我想要的不只是一张静态Excel而是一个能统计、能检索、能长期维护的个人资料库。于是就有了AnyPS5——一个完全跑在本地、不修改主机、也不依赖任何未公开接口的PS5游戏信息聚合工具。这个项目从名字就能看出定位任何一台PS5的玩家都可以用一套简单的数据录入方式把自己的游戏档案整理起来。它不是官方应用也不碰系统底层它做的是读数据、存数据、算数据这三件事。我在设计之初就给自己划了一条线所有数据都由玩家自己维护或从公开途径录入工具本身只做整理和呈现。这样既避免了各种授权问题也让整个项目可以脱离网络独立运行。1.1 三个最让我头疼的具体场景第一个场景是我到底买了什么。数字商店、实体光盘、会免入库、好友送的兑换码来源五花八门。主机上虽然有游戏库但它只告诉你有这个东西不会自动帮你标清购买日期、价格、来源渠道这些信息。第二个场景是进度到底到哪了。有些游戏我玩了一半搁置半年后想回去又不知道还剩多少内容更记不清奖杯拿到了什么程度。主机上的奖杯列表能看但看一次要点半天尤其游戏一多逐个翻完全不现实。第三个场景是我应该买还是该等。新游戏发售时总忍不住想冲首发但翻翻历史记录就会发现很多游戏半年后价格直接腰斩。如果有一个本地价格记录表每次想冲动消费之前先看一眼上一个类似游戏的降价曲线能省下不少冤枉钱。这三个场景有一个共同点主机本身不提供结构化输出但玩家自己心里是有这些信息的只是缺一个地方把它们记下来、算明白。AnyPS5就是冲着这个记录计算的缺口去的。1.2 项目边界一个做聚合与统计的工具这里必须说清楚AnyPS5不做什么避免有人看完文章以为它能跟主机实时通信或者更糟以为它涉及什么系统层面的改动。AnyPS5的数据源完全独立于主机内部存储。它的工作方式是玩家通过手动录入、批量导入模板、或者从官方公开的界面手动摘录数据把这些信息喂给工具工具负责清洗、去重、统计和可视化。它不连接主机不读取存档文件不做任何绕过机制的操作。从技术角度看它就相当于一个私人的、带统计能力的游戏账本。我把边界画这么清楚一方面是合规考虑另一方面也是工程上的合理性。主机系统是一个封闭环境与其绞尽脑汁去适配私有格式不如把精力放在数据建模、统计逻辑、使用体验这些自己真正能控制的地方。工具的价值在于让玩家用最少的操作维护一份结构完整的资料库而不是尝试去替代主机本身。1.3 哪些人适合照着这个思路自己折腾先说结论如果你只有三五款游戏完全不需要这个工具打开主机翻两下就找到了。AnyPS5的适用人群是游戏数量超过二十款、有整理习惯、希望用数据回顾自己游戏历程的玩家或者是对本地工具开发、数据建模感兴趣的开发者想找一个练手场景。对开发者的价值在于这个项目麻雀虽小但完整覆盖了数据采集、清洗、存储、统计、展示、备份这一整条链路。它不用高深的技术却能把很多基本功练扎实比如字符串编码处理、时区计算、去重逻辑、索引设计。后面章节我会把每一步的具体实现和踩坑过程都讲清楚。2. AnyPS5的数据管道不连主机也能把信息攒齐最开始我天真地想过能不能直接从主机系统里导出游戏列表试了一圈发现不行至少在不做任何额外操作的前提下不行。系统备份文件里确实有相关数据但格式没有公开文档吃了闭门羹之后我果断调整了方向——与其跟系统格式较劲不如把录入这件事做得足够舒服。2.1 三条数据采集路径的取舍我最终敲定了三条可以混合使用的采集路径每条路径适合不同场景。第一条路径是手动录入。打开一个只有几个字段的表单填游戏名、版本、状态、购买日期、价格、来源二十秒搞定一款。这条路径适合那些不常玩、只需要建档的游戏或者数字商店里免费入库的游戏——这类游戏往往没有实体盘的信息可查只能手动标记。第二条路径是批量导入。我先做了一个CSV模板列头跟数据库字段一一对应。如果玩家以前用Excel维护过游戏清单直接把旧表格整理成模板格式一次性导入上百条记录。我在这步投入了不少精力因为对老玩家来说手里大概率已经有一份或多或少的启蒙表格能把它们无损迁移进来这个工具才算真正立得住。第三条路径是截图辅助记录。奖杯信息没法批量导入我就按游戏逐个截屏然后用本机的图像识别辅助读出来识别结果进入确认界面人工修正一遍再确认入库。这个功能开发时挺有意思但坦白讲精度达不到百分之百中文成语类奖杯经常识别出错。我的处理是把OCR结果控制在候选值层面绝不直接写入数据库必须人工确认。2.2 三种路径混合使用的数据流三种路径不是互斥的实际使用时是流水线关系新游戏入库直接手动录入游戏基本信息状态默认未开始。一次导入旧数据CSV批量导入自动匹配已有记录匹配上就合并而不是重复插入。每周维护通关一款游戏后更新状态、补录时长拿了新奖杯就进奖杯页勾选。这套流程跑了一个月之后我最大的体会是工具能不能用起来很大程度上取决于录入成本够不够低。如果每次记一款游戏要填十个字段维护动力就没了但如果把大部分字段都设成下拉选项和默认值实际上每次只需要敲几个字数据就能保持鲜活。2.3 录入模板长什么样我做的批量导入模板是这样的结构列头和数据字段一一对应title,title_cn,region,genre,status,release_year,play_hours,purchase_date,purchase_price,source,rating,notes Baldurs Gate 3,博德之门3,asia,rpg,playing,2023,47.5,2023-09-06,398,digital,9,第一张地图探索中注意几个细节region用的是asia、europe、america、japan这类通用发行区标识不涉及具体国别status固定枚举值后面统计全靠它分组rating是1-10的整数字段可以用来算自己的平均评分。CSV模板的列顺序和数据库字段顺序一致导入时就不需要单独做字段映射了。3. 数据模型怎么设计一张主表加两张附属表数据采集想清楚了接下来就是存储。AnyPS5用的是SQLite这也是我反复比较后的选择理由后面会展开。整个库的核心是三张表games、trophies、price_history。3.1 games表游戏档案的核心字段games表是所有功能的起点字段设计决定了后续统计能玩出什么花样。下面是我最终定下来的结构字段名类型说明idINTEGER主键自增titleTEXT英文标题title_cnTEXT中文标题search_keyTEXT清洗后的检索用字段regionTEXT发行区域标签genreTEXT类型标签statusTEXT枚举值未开始/进行中/已通关/已白金/搁置release_yearINTEGER发售年份play_hoursREAL累计游玩时长小时purchase_dateTEXT购买日期ISO格式purchase_priceREAL购买价格sourceTEXT来源实体/数字/会免/赠品ratingINTEGER个人评分1-10notesTEXT备注search_key是我专门为检索加的冗余字段。它存储的是经过清洗的标题文本把全角字符转成半角、去掉空格、统一大小写。查询时在search_key上做匹配避免因为全角半角、中英文混排导致搜不到东西。这个字段占用不了多少空间但对检索速度和应用体验的提升非常明显。3.2 trophies和price_history进度与花费的过账记录trophies表记录每个游戏的奖杯获得情况。字段包括game_id外键、奖杯名称、描述、类型、是否获得、获得时间。设计上没有直接把所有奖杯拼在一个字段里而是拆成独立记录行因为后续要按最近获得时间未完成铜杯数量做筛选拆行才能用SQL直接算。CREATE TABLE trophies ( id INTEGER PRIMARY KEY, game_id INTEGER NOT NULL, trophy_name TEXT NOT NULL, description TEXT, tier TEXT DEFAULT bronze, achieved INTEGER DEFAULT 0, achieved_date TEXT, FOREIGN KEY (game_id) REFERENCES games(id) );price_history表是给等折扣这个场景用的每一行记录某款游戏在某一天的参考价格。表结构很简单game_id、record_date、price。平时每周手动维护一次看到商店调价就更新。有了这张表就能画价格曲线也能在下次想买游戏时翻出来给自己泼盆冷水。3.3 为什么选SQLite而不是其他方案我经常被问数据量这么小为什么不直接存JSON文件我的回答是JSON适合保存快照不适合做查询。一旦你想算平均游玩时长白金率按年份分组的总花费用JSON要么全量读入内存逐条算要么自己写一堆过滤逻辑而SQLite用一条SELECT就解决了。SQLite的另一个优势是零配置。它不需要安装数据库服务一个文件就是整个库备份时直接复制文件即可。AnyPS5作为一个个人工具我不想引入MySQL或PostgreSQL这种重量级组件也不想操心服务进程的启动停止。Python标准库自带sqlite3模块不用装第三方包就能跑通全链路这对可复现性来说太重要了。4. 核心功能的实现细节清洗、统计与本地检索这章写代码层面的具体实现。AnyPS5的后端逻辑用Python标准库完成前端输出成静态HTML页面全程没有引入重量级框架。4.1 数据清洗先让数据长成同一个样子不管手动录入还是CSV导入原始数据一定是脏的标题前后有看不见的空格有些用户输入了全角字符有些英文标题大小写不一致。如果把这些数据直接存进库后面检索和去重都会出问题。我的清洗函数是这样设计的import re import unicodedata def clean_search_key(raw: str) - str: # 全角转半角 normalized unicodedata.normalize(NFKC, raw) # 统一转小写 normalized normalized.lower() # 去掉所有空白字符 normalized re.sub(r\s, , normalized) return normalized这个函数对所有标题执行四步操作全角转半角、转小写、去空白、保留字母数字。清洗后的结果存入search_key字段。录入新游戏时先调用这个函数生成检索字段再去库里比对是否重复如果匹配已有记录就弹出合并确认而不是新增一条。这段代码看似简单却解决了我最头疼的重复录入问题。比如Baldurs Gate 3和博德之门3这两个输入经过清洗后一个变成baldurlsgate3另一个变成中文串显然不同但英文标题如果存在Baldurs Gate 3这种多余空格的情况清洗后就能正常匹配。没有这层处理去重逻辑完全是空中楼阁。4.2 统计口径白金率、时长中位数和年度费用存储结构定了之后统计就是SQL查询的事。但统计口径需要认真定义直接用现成函数很容易算出没意义的数字。以白金率为例。如果按白金数量/全部游戏数量算把几十张会免入库的玩过五分钟也算进去白金率会低得离谱。我的口径是分母只算已开始游玩的游戏也就是status不是未开始的记录。分子是白金数量。这个口径更真实地反映我手上这些认真玩过的游戏有多少真正通关了。SELECT COUNT(*) AS total_played, SUM(CASE WHEN status 白金 THEN 1 ELSE 0 END) AS platinums, ROUND(100.0 * SUM(CASE WHEN status 白金 THEN 1 ELSE 0 END) / COUNT(*), 1) AS plat_rate FROM games WHERE status ! 未开始;游玩时长同理平均值容易被个别几百小时的特殊情况拉偏我改用中位数。SQLite没有内置中位数函数需要借助子查询SELECT AVG(play_hours) FROM ( SELECT play_hours FROM games WHERE play_hours 0 AND status ! 未开始 ORDER BY play_hours LIMIT 1 OFFSET (SELECT COUNT(*) FROM games WHERE play_hours 0 AND status ! 未开始) / 2 );这个写法的思路是排序后取正中间那一条记录的数值对奇数条是拿中间值对偶数条跳过一半取后一条实际用下来足够准确。年度费用统计更简单purchase_date字段用substr(purchase_date, 1, 4)提取年份然后GROUP BY年份累加purchase_price。4.3 本地全文检索先用LIKE再考虑要不要上分词AnyPS5的检索需求很明确玩家输入一个游戏名中文、英文、简称都行快速定位到对应卡片。我的实现分两层。第一层直接查search_key字段SELECT * FROM games WHERE search_key LIKE ? || %;这个查询利用了SQLite的索引机制配合前缀匹配性能对于几千条记录来说绰绰有余。但它的局限在于不支持关键词出现在中间的情况。比如玩家只记得Gate 3这几个词搜gate能匹配搜baldurs gate也能匹配因为两者都是前缀但如果搜索词来自标题中部就无法命中。第二层做了一个简单的分词兜底把搜索词按空格拆成多个词每个词单独做前缀匹配要求至少命中一个。这个方案并不复杂但已经覆盖了我日常使用中绝大多数场景。中文标题的检索就更简单了——中文字节串直接做子串匹配SQLite的LIKE %关键词%在中文环境下表现可以接受。真要再进一步就得引入分词库了但对几百到几千条游戏记录来说收益不大我把这个留给将来扩展。5. 可视化与日常使用把本地页面当成游戏控制台数据存好、能查能算还差最后一步怎么把这些结果呈现出来。我不喜欢每次查询都打开终端敲SQL所以AnyPS5会生成一个静态HTML报告放在本地目录里浏览器打开就是整个仪表盘。5.1 仪表盘上放的四组核心指标首页顶部放四张数字卡片分别是游戏总数、已白金数量、平均通关时长中位数、累计花费。四张卡片的数据对应四条SQL每次生成页面时自动计算不写死任何数字。接下来是按状态分组的条形分布比如进行中12款、未开始30款、已白金8款、搁置15款这个分布能直观反映游戏库的健康程度如果未开始数量远大于进行中说明买游戏的速度又超过了玩游戏的进度是时候收手了。再往下是年度购买频率表2023年买了多少款、花了多少钱2024年对比上年是涨是跌。别小看这张表它是我克制冲动消费最强有力的工具——看到前一年的数据很多想下手的游戏就冷静了一半。最后是最近玩过的游戏列表。这个列表依赖last_played_date字段按最后游玩时间倒序排列。只要导入时顺手填了这个日期这个列表就一直是最新的。5.2 游戏详情页的设计细节点击游戏卡片进入详情页里面包含三块内容基本属性、奖杯进度、价格历史。基本属性直接展示games表的字段配一个状态下拉框改完状态后一键保存回数据库方便终于通关了这种时刻快速更新。奖杯进度用进度条显示百分比用已获得奖杯数除以总奖杯数。往下是未完成奖杯的列表会按最近获得时间从早到晚排序这样一眼就能看出哪些奖杯搁置时间最久。价格历史画成简单折线图。我第一版直接用Python生成图片太麻烦后来换成了纯HTMLJavaScript的轻量绘图方案价格数据存成JSON数组页面里用几十行原生代码画折线。没有引入图表库页面加载速度快维护也简单。5.3 多设备之间怎么同步AnyPS5是纯本地工具多设备同步的场景本来是没有设计的。但我实际使用中确实遇到了书房电脑和笔记本切换的需求解决办法出乎意料地朴素SQLite数据库文件本身就是可迁移的。我每次维护结束执行一次备份操作工具会把数据库文件和当前HTML报告打包成一个带时间戳的压缩包。换设备时把压缩包解开打开启动脚本工具会先检测数据库文件是否存在存在就直接读取不存在就自动建空库。整个过程不用配置任何服务器我用网盘的常规文件夹同步功能把这几个备份文件同步到其他设备然后手动解压最新包就行。这个方案谈不上优雅但它符合AnyPS5本地优先、零依赖的定位。在个人工具这个尺度上文件即数据、数据即备份是最不容易出错的做法。6. 开发中踩过的坑字符串、时区、版本区分AnyPS5功能不算复杂但开发过程中我遇到了不少跟游戏数据特点强相关的坑。挑三个最典型的展开讲这几个问题如果没注意后续维护成本会明显上升。6.1 游戏标题里的隐形字符第一个坑是游戏标题里的不可见字符。很多从网页复制的游戏名看着没问题实际字符串里夹着不换行空格、零宽空格这些字符。早期版本没有清洗逻辑时我遇到过同一款游戏录了两遍但程序认为它们不同的迷惑现象排查了好久才发现是两个不可见字符在作怪。解决办法就是前面写的清洗函数。对所有文本字段统一走NFKC规范化这个处理会把全角符号转半角、把多种空格统一为普通空格再配合正则彻底去掉空白基本能消灭这类问题。建议所有做本地记录类工具的朋友在数据入口加一道清洗宁可多算一步也不能让脏数据进入库。6.2 奖杯获得时间的时区陷阱第二个坑跟时区有关。录奖杯时间时一开始我直接记录了系统给出的时间比如2024-11-10 23:30:00。后来有一次跨时区出差后打开工具发现最近获得的奖杯时间看起来整整晚了好几个小时怎么想都不对最后发现原始时间被系统当作UTC时间处理了。正确的做法是任何时刻的录入都统一先转成UTC再存库展示的时候再转回本地时区。所有日期时间字段我都建议用ISO 8601格式加时区后缀来存储例如2024-11-10T23:30:0008:00。虽然SQLite的日期函数对带时区字符串的支持有限但保证存储格式规范至少不会出现数据本体不可追究的问题。6.3 同一款游戏的多个版本怎么区分第三个坑是标准版、豪华版、年度版的共存问题。如果库里同时有标准版和豪华版直接按标题检索会出现多条记录如果不做严格区分统计游戏数量时就会重复计算价格和时长也全混了。我的处理方式是在数据模型中增加一个可选的edition字段区分标准版、豪华版、实体铁盒版等同时给标题加上更明确的槽位比如God of War Ragnarok后面加括号标注版本。去重逻辑用search_key edition两个字段拼接后的值判断同一款基础游戏的不同版本就会显示为并行记录既保留完整性又不会被统计口径误伤。这里再补充一个经验任何去重逻辑都不要忽略同款游戏不同代的情况。比如同一系列名称的二代、三代标题可能只差一个数字如果不按完整标题精确匹配很容易把两代混成一条。我在录入界面特意加了一行提示要求边录边填发售年份这样遇到争议记录至少能用年份做二次判断。7. 一点个人体会和相关经验最后说几句实在话。AnyPS5做下来我最大的收获不是代码量而是想明白了一个道理个人工具能不能长期用下去核心不在功能多而在录入成本和维护频率的平衡。如果每次维护要花十五分钟以上用不了两周就会放弃但如果把高频操作压到三秒内它就会变成顺手记录的习惯。现在我的日常流程很简单买了新游戏顺手录一条通关了改个状态每周花五分钟刷新价格和奖杯再点一下生成报告。整个过程不需要连主机不需要依赖别人提供的服务任何一步断了都不影响其他数据。这就是我想要的状态。如果你也想做类似的项目我的建议是不要一开始就追求自动同步、智能识别这些花哨能力先把基础的数据模型设计好把录入体验做顺跑起来之后再考虑要不要加OCR、要不要做更花哨的图表。工具是给后面两三个月的自己用的记得留一条就算半年没打开也能快速恢复的后路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询