☰
自制AnyPS5:跨区PS5游戏库管理与价格追踪工具实践
2026/10/15 23:58:15 网站建设 项目流程

如果你手里有一台 PS5,而且玩了几年,游戏库大概率已经乱成一锅粥:港服、日服、美服各领过一堆会免,实体盘和数字版混在一起,哪些通关了、哪些烂尾了、哪些价格已经跌到谷底,完全凭记忆。我一开始想找现成工具来管,结果不是要装手机 App 单账号绑定,就是只支持美服数据,换一个区就得重新折腾。后来干脆自己写了一个小工具,名字就叫AnyPS5。

它本质上是一套“个人游戏数据管理面板”,把你在各个账号下看到过的游戏信息导入进去,然后自动去重、补全、统计时长、追踪价格,最后用图表告诉你:你真正玩得最多的游戏是什么,哪些游戏买完根本没碰,哪个跨区的会免游戏现在打折力度最大。它不修改主机系统、不碰任何灰色操作,数据来源就是你账号里能看到的信息和商店公开页,适合所有想把游戏库打理清楚的普通玩家,也适合想学 Python/前端数据项目的开发者参考。

1. 我先梳理一下需求:AnyPS5 到底要解决哪几个具体问题

1.1 痛点一:跨区账号的游戏库信息根本没有聚合视图

PS5 主机本身能看游戏管理界面,但一旦你注册了多个服区的账号,情况就复杂了。比如我日常玩港服主力账号,遇到日服独占试玩会去日服领一下,美服偶尔买打折数字版。这样一来,三个区各有一套游戏列表,主机界面只会展示当前账号的内容。想统计“我总共有多少个游戏”,只能手动切换账号,一张一张截图,再拿 Excel 手工整理,麻烦不说,隔几个月就失效了。

AnyPS5 的思路是把“游戏条目”作为最小存储单元,每条记录带上你手动导入时填写的所属账号和商城区域。导入之后,不管数据来自哪个区,最终都会合并到同一个视图里。我只需要一次导入,之后每次新增游戏都能自动归类,不用再切账号去数。

1.2 痛点二:游戏状态完全靠记忆,通关率、进行中、烂尾没有量化

另一个很现实的问题是:买了游戏不代表玩了,玩了也不代表通关。我之前尝试用 Notion 记录通关状态,但坚持不到两周就放弃了,因为开 PS5 之前还要打开手机标记一条记录,这个动作太重了。

AnyPS5 把状态管理做成了“默认待定”,每次导入时你可以给游戏打一个标签。我还做了快捷批量操作,比如“本周打过一次”会自动把游戏打上“游玩中”标签,如果 30 天内没有再次启动的痕迹,则自动降级成“吃灰”。这些状态变化全部基于时间戳,不需要你每次手动写笔记。日常使用中,我只需要在导入时偶尔标注“已通关”“已放弃”,其余交给标签规则去算。

1.3 边界设计:只读数据,不做系统级操作

项目一开始就有个底线:不做任何需要连接主机系统、访问主机文件或绕过验证的功能。原因很简单,一是我不想因为一个工具给自己添麻烦,二是我觉得游戏数据管里的价值,完全可以靠“玩家可见的数据 + 商店公开页”实现,没必要去碰敏感区域。

所以 AnyPS5 的数据源是这三类:你自己从商店页面或 PS App 截图后手动录入的 JSON/CSV、你从第三方数据站导出(或手工整理)的游戏价格与元数据、以及定时抓取商店公开页面的试用版商品信息。这三个来源都不需要任何特殊权限,你只是把你本来就能看到的内容交给程序去整理。

理论上也可以扩展成直接解析官方 App 的背包接口,但那个方向需要处理登录态和验证,安全和稳定性都不好保证。我最终放弃了这个方案,也建议你自己做类似项目时,先想清楚“哪些数据拿得合法且稳定”,再决定技术路线。

2. 技术选型:一套能长期维护的轻量方案是怎么定的

2.1 后端框架:选 FastAPI 而不是 Flask

项目最开始我用的是 Flask,因为以前写小工具都靠它。但写着写着发现,AnyPS5 的核心功能不是页面渲染,而是对接数据导入、定时任务、API 请求,这些场景对类型校验和异步支持很敏感。Flask 并不是不能做,但对我来说,遇到 Python 3.10+ 的match语句和asyncio时,FastAPI 的集成体验明显更好。

FastAPI 自带的 Pydantic 可以把每条游戏数据的字段校验放在入口处,比如要求price必须是数字,release_date必须是日期,如果用户导入的 CSV 里有脏数据,接口会直接返回哪一行哪一列出问题,而不是等到数据库阶段才崩溃。这一点在多人试用时特别有感知,很多人填表格就是喜欢乱写格式。

2.2 存储方案:SQLite 足够,不需要上 PostgreSQL

前期我认真考虑过 PostgreSQL,因为后面可能要存每个游戏的历史价格曲线,数据量会膨胀。但算了一下:如果有 1000 个游戏,每小时抓一次价格,一天就是 24000 条记录,一年约 876 万条价格记录。SQLite 在 WAL 模式下,处理这个数量级其实非常稳,尤其是单机部署、几乎没有并发写入的场景。

最终我采用了 SQLite + WAL,一个库文件,备份也非常方便。数据库里面最核心的只有三张表:games存游戏基础信息,prices存历史价格快照,play_records存游玩状态记录。为了防止膨胀,prices表超过 90 天的数据会自动聚合到日平均,之后删除原始每分钟快照。这个设计看着简单,但让我避免了引入一套分布式数据库带来的运维成本。

2.3 前端可视化:Vue 3 + ECharts,图表比表格更有用

给游戏数据做前端,最容易犯的错就是把所有字段做成一个超级表格。刚开始我也是这么干的,但发现数据一多,表格根本没法看:条目太多要滚动很久,而且很难一眼看出哪些游戏价值最高、哪些最该清理。

后来引入了 Vue 3 + ECharts,把统计视图改成四个卡片和两个大图表。卡片展示“游戏总数”“预估总花费”“平均通关率”“本月游玩时长”,大图表分别是“按类型的数量分布”和“最近半年每月游玩时长”。这些图表不是炫技,而是真正把数据变成了可决策的信息。比如我看到“平均通关率不到 30%”,就会意识到自己买太多了,以后会优先清库存而不是买新游戏。

2.4 部署:Docker Compose 只做了三件事

我选择 Docker 部署,不是为了云原生,而是图省事。

docker-compose.yml里包含了后端容器、前端静态文件服务、一个定时抓取任务的容器。其中定时任务没有用系统 cron,而是单独跑一个轻量容器,里面用 APScheduler 控制每 6 小时抓取一次商店价格。整个部署目录非常简洁:

anyps5/ ├── backend/ # FastAPI 应用 ├── frontend/ # Vue 3 构建产物 ├── scripts/ # 导入脚本和定时抓取 └── docker-compose.yml

后端只监听 127.0.0.1,前端 Nginx 反代,避免暴露过多端口。数据库文件挂载到本地目录,备份时直接复制文件就行。这套流程我用了半年,除了偶尔要手动清一下历史价格,基本没有运维负担。

3. 核心功能拆解:导入、清洗、合并、统计一条链怎么做

3.1 导入路径设计得简单,才能坚持用下去

游戏数据管理工具最大的敌人不是功能不够,而是懒得录数据。我在 AnyPS5 里做了三种导入方式,覆盖不同场景。

第一种是手动添加:按表单填名称、平台、购买日期、价格、状态,适合一次性录入二三十个实体游戏。第二种是 CSV 批量导入:我会在项目里提供一个模板,包含name、region、price、status、playtime_minutes等字段,适合从旧表格迁移数据。第三种是从现有的游戏收藏平台通过浏览器插件导出数据,再上传进 AnyPS5。

实际用起来,大部分人最喜欢的是 CSV 导入,因为可以把以前在 App 里收藏的游戏一次性拖进来。我给 CSV 解析写了一个比较健壮的处理流程:先按字节流判定编码,兼容 UTF-8 和 GBK;接着按列名做模糊匹配,比如标题、游戏名称、name、Title都能识别为同一个字段。这些细节看起来小,但如果没有,用户第一步就会卡住。

3.2 游戏信息规范化:一套笨规则胜过单点智能

导入数据最怕同名游戏被当成两个不同条目。比如《暗黑破坏神 4》在不同平台上有不同版本,中文名和英文名也不一样。我本地上没有现成的主数据库可用,所以做了一套“笨规则”:

  • 所有游戏名称先去空格、全角转半角、去除末尾的“中文版”“英文版”字样。
  • 用“名称 + 平台”作为唯一键,但平台字段只保留“PS5”“PS4”“跨平台”三个枚举值。
  • 如果疑似重复,不自动合并,而是标记为“待确认”,在管理界面里并列显示,让用户手动选择是否合并。

这套规则解决了我 95% 的重复问题。剩下 5% 可能是因为同一个游戏的中英文名差异太大,比如《Persona 5 Royal》和《女神异闻录 5 皇家版》,这种我干脆手动合并,毕竟只有几十次操作,不值得为了它去对接外部数据库。

3.3 通关率与时长的统计逻辑

通关率不是简单拿“已通关数量 / 游戏总数”来算,因为有些游戏是多人游戏,根本没有“通关”概念,还有些运动会免游戏只是拿来试玩,不应该计入分母。

我的统计口径是:分母为所有标签为“通关”“烂尾”“进行中”的游戏,分子为“通关”,会免试玩和体育竞技类默认不计入分母。这个口径你可以自己在配置里改。时长统计则更简单,每条游玩记录都有一个started_at和ended_at字段,导入时如果缺失,会根据你手动填的playtime_minutes反向拆分。

为了展示更有价值的指标,我还加了一个“游戏时长/价格比”的计算。用一条游戏的总游玩分钟数除以购买价格,得到一个“每元小时数”。比如一个 300 元的游戏玩了 30 小时,就是每小时 10 元;一个 50 元的打折游戏玩了 10 小时,就是每小时 5 元。这个指标不一定科学,但很适合用来做自我提醒。

3.4 价格追踪与降价提醒

价格追踪最初是让我坚持做 AnyPS5 的原因。因为我发现很多游戏在港服和日服的折扣节奏完全不一样,有的港服 6 月打过折,日服 9 月才跟上。如果有一个工具能记录每个区服的公开价格快照,就能判断“现在是不是该入手”。

AnyPS5 的定时任务每天固定时间访问商店公开搜索页,用 HTML 解析里面带.ps5标签的商品卡片,提取名称、价格、折扣率、优惠截止时间。解析需要处理很多小问题,比如价格单位、币种符号、折扣文案,不同区服格式不同。我这里做了一个统一的中间结构:

{ "name": "示例游戏", "region": "HK", "currency": "HKD", "original_price": 568.0, "discounted_price": 398.0, "discount_percent": 30, "last_seen_at": "2025-06-18T12:00:00+08:00" }

降价提醒是通过每个游戏的历史最低价对比实现的。每当新抓取的价格低于历史最低价,就记录一条通知事件。我会把这些事件汇总成每周邮件摘要,不会实时推送,因为游戏打折是常态化事件,没必要天天焦虑。

4. 踩坑实录:从时间戳到分页,再到前端卡死

4.1 时区问题:统计时长差一小时,导致月度报表少了几小时

第一次写完月度报告后,我发现无论是自己导入的时长还是系统记录的开始时间,只要跨过凌晨就会差一个小时。排查了半天,原因是所有时间字段存的是 ISO 字符串,但采用了本地时区,没有统一转成 UTC。

后来我规定:所有时间进入数据库之前,必须由后端统一转换成UTC+08:00为基准的datetime存储,展示时再由前端根据浏览器的时区做转换。但考虑到大部分用户都在国内,反而 UTC+08:00 就是他们的本地时区。为了更通用,我干脆把所有时间都转成 Unix 时间戳整数,查询时再格式化。这样无论从哪个时区导入,存进数据库的永远是绝对时间,统计口径不会再偏离。

4.2 分页抓取导致的重复数据:同一个游戏被记录了三次

商店公开页的搜索接口有一个默认分页参数,我一开始没有处理“已经抓取过的商品”,结果同一个游戏在三个不同页面上出现了很多次,导致价格历史表瞬间塞满了重复记录。

修复的方法非常简单:抓取时把每个商品的名称和名称哈希值一起存入临时表,在进入价格表之前做唯一性检查。但真正让我记住这个坑的是“为什么同一个游戏会有多个 URL 入口”。有些商店页面会把同款商品的不同版本分别展示,比如标准版和豪华版,名称相同,但 URL 不同。如果只按名称去重,会把豪华版和标准版合并成一个条目,丢失版本信息。最后我只能用“名称 + 版本号”作为复合去重键,并且对无法识别的版本打上“未知版本”标记。

4.3 前端渲染 8 万行数据的卡顿:Grid 组件救了我

一开始我把所有游玩记录放到一个表格里,有 8 万多条数据,页面打开要 4 秒,滚动起来像幻灯片。我用过虚拟滚动组件,第一版只做了窗口滚动,但表格内部有横向滚动,导致纵向虚拟滚动失效。

最终换了方案:把“游玩记录”和“游戏列表”拆开,游戏列表最多几百条,游玩记录只展示最近 1000 条,并提供按月份筛选、导出 CSV 按钮。这样既保住了细节数据,又不会让浏览器崩溃。另外,我总结出了一个教训:不要高估浏览器一次性渲染数据的上限,尤其是当每行里面还带有缩略图、标签、操作按钮的时候,虚拟滚动并不能解决所有性能问题,最好的方案是“只加载你需要的”。

4.4 SQLite WAL 模式下备份数据库的隐性坑

WAL 模式确实让并发读写顺畅了很多,但它会把未合并的数据写在-wal文件里。我一开始备份时只复制了主数据库文件,结果恢复之后少了一天数据。后来我修正了备份脚本:备份前先执行一次PRAGMA wal_checkpoint(TRUNCATE);,再复制数据库文件。如果你也用手工备份 SQLite,这是最容易忽略的一点。

部署环境中我配置了一个每天凌晨 3 点的备份任务,把数据库文件、前端配置、导入日志打包到一个带日期的压缩包里。因为有定时抓价格的任务,价格表会持续写入,备份时必须注意不要复制到写入一半的文件。用sqlite3的 backup API 可以避免这个问题,但单机场景下wal_checkpoint已经够用。

5. 这个项目后续能扩展的地方,以及我个人的实际感受

5.1 多平台扩展的思路

用 AnyPS5 管理游戏数据的逻辑,其实不限于 PS5。如果你玩 Switch 或 PC 平台,也一样有跨区账号、打折通知、通关状态这些需求。

我在设计数据库字段的时候预留了一个platform字段,目前只取PS5和PS4,但扩展成Switch、Steam并不难。比较麻烦的是数据源不同,Switch 没有公开的价格页可以轻易抓取,Steam 也很容易因为频繁请求被限流。如果你想把项目扩展到多平台,建议先从“用户手动导入为主、自动抓取为辅”开始,不要一开始就追求全平台自动化。

5.2 移动端适配和消息推送

现在 AnyPS5 的前端是基于 PC 的 Dashboard,虽然基本响应式,但手机上只能看,不能录入。后续我想做一个移动端友好的表单页,专门用来快速录入“今天新买的游戏”和“刚刚通关的游戏”。降价提醒目前只能邮件,后续计划接入微信机器人或者 Telegram Bot,这样通知更即时。

不过移动端方案会增加项目复杂度,尤其是登录鉴权和通知通道配置,目前只有我一个人用,所以暂时没有重做前端的想法。如果你要自己跑起来,个人用不考虑移动端也没关系。

5.3 从一开始就坚持“不自动化”的部分,反而最稳定

最后说一点个人体会。AnyPS5 整体看起来是个自动化工具,但我在关键路径上刻意保留了很多手动操作,比如手动标记“通关”,手动合并疑似重复游戏,手动刷新某条游戏的价格。这些手动操作不是因为我懒,而是因为自动化在这几个场景里的错误代价太高。

一个自动化的降价提醒如果多推了三五次,你只会觉得烦;但一个自动化的“游戏状态判断”如果误判了你的喜爱游戏,你会对整套系统失去信任。现在的规则足够简单,让我用了一年多还没产生“舍弃”的念头。工具不需要大而全,只要在你打开它的时候,能比上一次更懂你的游戏习惯,就已经值得维护了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询