
PS5买回家三个月后我发现自己陷入一种完全没预料到的状态不是没游戏玩而是被“管理”这件事逼疯了。数字版买了几十个、实体盘散在架子上哪个游戏玩到哪一步、哪些白金了、哪些烂尾了全凭记忆存档只敢依赖PSN云端会员一到期就心虚想等打折入手的游戏每天刷新商店页面也烦。于是我用业余时间写了一套本地工具取名AnyPS5。这个名字没多玄乎我的意思是“任何想整理PS5数据的时候都有个顺手的东西能用”。如果你也跟我一样PS5的底座成了吃灰容器游戏库越滚越大但越来越难找存档和价格总得靠人肉记忆那这篇文章应该对你有用。我会把这个项目的完整设计思路、技术选型、模块实现和踩坑过程全部拆开讲。它不涉及破解、不碰固件、不依赖任何私有接口只是把一个普通玩家手里本就可以合法访问的数据整理成自己能看懂的本地数据库。1. 一开始为什么写AnyPS5PS5玩家的零散需求清单先说清楚我不是专业的主机开发者只是一个玩了十几年游戏、平时也写Python的普通玩家。买PS5之前我以为痛点只有一个游戏贵。买之后才发现真正的痛点是一堆琐碎需求没人管。1.1 我遇到的四个高频场景第一个场景是游戏库管理。数字版、会免、实体盘三种来源混在一起主机自带的入库列表只能显示“已安装”和“已购买”但没法回答一个问题我到底有哪些游戏哪个通关了哪个烂尾了我甚至出现过在商店里差点二次购买已拥有的游戏这种尴尬事。第二个场景是存档备份。PS5把存档管理做得比PS4还保守本地能直接导出的信息非常有限。我习惯把重要游戏通关前的存档单独备份到U盘但经常忘记哪个游戏上次备份是什么时候。会员云存档虽然方便但有一次续费空窗期让我意识到把重要存档全押在订阅服务上不踏实。第三个场景是价格监控。我想买的游戏通常有五六个分布在港服、日服和欧美服每个服打折时间不一样。手动刷新商店页面太原始而现成的价格追踪网站要么延迟大半天要么只覆盖少数区域商店。第四个场景是外设兼容性记录。我手上有DS5手柄、旧DS4、方向盘、键鼠转换器还有给娃用的副手柄。每台设备在不同游戏里的兼容表现不一样比如某方向盘在GT7里是完美的在《F1 23》里却按键错乱。这种记录用记事本太乱放表格里又不方便查。这四个需求单独看都不大但凑在一起就是一团乱麻。我也试过用Excel硬扛最后发现维护成本高到离谱。1.2 市面已有工具的现状数据都散着其实市面上不是没有工具问题在于它们是“碎片化”的。官方PS App能看在线状态、购买记录和奖杯但没法本地存档备份提醒也没法做价格历史追踪第三方奖杯网站数据翔实但只覆盖奖杯不关心价格和外设价格追踪APP又跟游戏库完全不打通。我整理过一张对比表越整理越觉得差距明显能力官方PS App第三方奖杯站价格追踪工具AnyPS5目标游戏库全量管理部分部分否是本地存档快照登记否否否是多商店价格历史否否部分是外设兼容记录否否否是想看的维度自由组合否否否是表格最能说明问题我要的不是一个“奖杯神器”或者“打折雷达”而是一份属于自己的、能互相联动的PS5数据底账。1.3 工具定位本地优先一切数据归自己想清楚需求之后我给AnyPS5定了三条规则。第一本地优先所有数据放进本地SQLite库不上传、不依赖云端账号第二不做破解扩展不读取主机内部文件只处理玩家自己通过U盘、截图、公开商店页面能拿到的信息第三模块插件化游戏库、存档、价格、外设各自独立互不影响。这个定位让我写代码时非常舒服。我不需要跟任何私有协议搏斗也不用担心工具被人拿去搞灰色用途。它本质上是一个“玩家自己的信息整理系统”安全、干净、合法。2. AnyPS5的总体设计与技术选型一个本地优先的采集型工具集定位清楚之后剩下的就是搭骨架。AnyPS5不是一个单一功能的脚本而是一个多模块的本地工具集所以我花了比较多时间在项目结构上。2.1 项目结构单仓库多模块项目叫AnyPS5代码仓库里大概是这样的结构anyps5/ ├── app/ │ ├── main.py # FastAPI入口 │ ├── models.py # SQLAlchemy模型 │ ├── modules/ │ │ ├── library/ # 游戏库与奖杯截图OCR │ │ ├── backups/ # 存档备份登记 │ │ ├── prices/ # 商店价格监控 │ │ └── peripherals/ # 外设兼容记录 │ ├── static/ # 前端面板资源 │ └── templates/ # Jinja2页面 ├── scripts/ │ ├── ocr_pipeline.py # 截图OCR任务 │ ├── price_watch.py # 价格定时抓取 │ └── backup_reminder.py # 备份提醒 ├── data/ │ └── anyps5.db # SQLite数据库 └── requirements.txt每个模块都可以单独跑OCR管道是独立脚本价格监控是独立定时任务Web面板只是把结果统一展示出来。这样有一个明显好处任何一个模块挂了不影响另外几个。2.2 技术栈选型为什么是PythonFastAPISQLite技术栈我几乎没有犹豫。Python处理OCR、爬虫、文本处理是舒适区FastAPI用来起一个轻量Web面板很顺手SQLite对于单用户的几千条记录完全够用还不需要装数据库服务。可能有人会问为什么不用Electron做个桌面应用我的理由是Electron打包体积太重量级而且我要的不是一个“软件”是一个能随时加脚本的底座。做成Web面板的好处是手机上通过局域网也能打开PS5开着的时候顺手查一下价格、看备份提醒体验很好。SQLite在这个项目里其实被低估了很多人觉得它只能做玩具但搭配WAL模式和合理的表结构完全能扛住价格表几万条记录。后面我会专门讲我踩过的锁库坑。2.3 数据模型设计的核心一切围绕“游戏”这个主语整个数据集的核心是游戏表其他模块都跟它关联。我建了几个主要表games游戏基础信息(id, title, platform, source, status, first_seen_at)save_backups(id, game_id, backup_time, backup_path, note, md5)price_records(id, game_id, store, region, price, currency, fetched_at)peripherals(id, name, type, interface, compatibility, note, updated_at)这样的好处是价格记录可以追溯历史存档快照可以关联到具体游戏外设虽然跟游戏不是强关联但我在表里加了一个“适用游戏列表”的字段用JSON存。下面是建表的SQL简化版CREATE TABLE games ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, platform TEXT DEFAULT PS5, source TEXT DEFAULT digital, status TEXT DEFAULT backlog, first_seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE save_backups ( id INTEGER PRIMARY KEY AUTOINCREMENT, game_id INTEGER NOT NULL, backup_time TIMESTAMP NOT NULL, backup_path TEXT, note TEXT, md5 TEXT, FOREIGN KEY (game_id) REFERENCES games(id) );所有数据的血缘都很清楚后面做统计面板时可以直接JOIN不用绕弯路。3. 游戏库与奖杯数据整理用OCR打通PS5截图这座孤岛游戏库整理是整个项目里我最想说的模块因为它的方案选择很能代表AnyPS5的思路不硬啃私有接口而是从玩家手头已有的截图上想办法。3.1 核心思路不依赖官方接口从截图下手PS5主机自带截图功能奖杯解锁、游戏启动、游戏完成度这类画面系统都会生成包含游戏名称、图标、进度信息的卡片式截图。把U盘插到PS5上可以整批复制这些截图到电脑。这是索尼官方支持的导出方式完全合法。那有了截图之后怎么变成结构化数据我的答案是OCR。用现成的开源OCR库把截图里的游戏名、奖杯类型、时间信息识别出来再归一化进数据库。这比去逆向PSN接口成本低得多而且不碰任何账号风险。3.2 OCR管道的具体流程整个管道分四步截图拷贝、图片清洗、文本识别、结果归并。截图拷贝没什么好说的。图片清洗我这里做了一件重要的事先把截图统一缩放到合理分辨率然后裁剪掉屏幕上下的无效区域只保留中间的游戏信息卡片区域。这个裁剪规则我调了很久不同版本的系统UI卡片位置略有差异我干脆做了一个“版本号坐标”的配置表。识别部分我用的是PaddleOCR因为中英文混合游戏名比较多这一版引擎在中文识别上的表现比Tesseract好很多。核心代码并不复杂from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch) def ocr_screenshot(img_path): result ocr.ocr(str(img_path), clsTrue) texts [] for line in result: for item in line: texts.append(item[1][0]) return texts识别出来的文本会进入一个归一化模块。这里的坑在于游戏名写法不统一“God of War Ragnarok”可能被识别成“God of War Ragnarök”日版游戏还带日文副标题。我在库里建了一张别名表把“游戏规范全名”和“常见OCR变体/简称”映射起来然后用编辑距离匹配兜底。3.3 奖杯统计与游戏库标签体系OCR识别出来的并不仅仅是游戏名。PS5的奖杯截图里通常包含“已获得奖杯数/总数”和奖杯等级信息。我把这些字段也提取出来写成结构化的奖杯进度记录。与此同时我给游戏库设计了一套轻量标签体系backlog(没开始)、playing(进行中)、completed(已通关)、platinum(已白金)、abandoned(烂尾)。每次OCR扫到某个游戏的新截图就会自动更新它的状态。比如识别到奖杯进度100%我会标记为platinum候选再人工确认一次。这个机制跑起来之后效果很直观。我第一次扫描自己U盘里的两千多张截图居然识别出14个“我记忆中早通关但其实烂尾”的游戏数据跟印象冲突的时候感觉很有意思。3.4 OCR落地时的头疼问题误识别、时区和重复图片误识别是OCR必然面对的问题。我遇到过把“FINAL FANTASY XVI”识别成“FINAL FANTASY XV1”的情况还有把游戏内角色名的英文压到游戏名上面的问题。解决办法是所有命中结果都先跟别名表比对相似度低于阈值的进入“待确认队列”不会直接污染主表。第二个问题是时区。截图OCR识别出的是文本时间但PS5系统时间是UTC还是北京时间取决于你的设置。我在导出截图时写了一个小脚本优先读取图片的EXIF时间戳而不是OCR文本里的时间这样避免了反复调整配置。第三个是重复图片。同一款游戏我可能截图几百次如果不做去重游戏库会变成灾难。我在管道的入口处先算每个图片的md5哈希重复图片直接跳过。代价是哈希计算需要一点时间但跟后续省下的手工清理成本比完全值得。问题我的最终方案游戏名OCR变体别名表编辑距离匹配时间源不一致用EXIF时间戳不用OCR文本重复截图图片md5全局去重不确定的记录进待确认队列不自动入库4. 存档备份提醒与本地快照管理让数据安全变得可感知存档备份这个模块本质上不是“帮你备份”而是“提醒你什么时候该备份并记录你备份了什么”。原因很简单PS5官方只允许通过系统设置的“已保存数据”把存档复制到U盘任何绕过这个流程的第三方存档管理手段都可能涉及越狱或私有协议我不想碰。4.1 云存档的隐患与本地备份的必要性很多人觉得开两年PSN会员就能高枕无忧但我算了笔账会员到期后云端存档只保留一段时间超过期限可能被清掉更麻烦的是如果某天你在本地启动了一个旧版本游戏PS5自动同步云存档时可能把新进度覆盖掉。这种事情一旦发生哭都来不及。所以我坚持一个朴素的习惯重要游戏通关前、更新大版本前、会员到期前各做一次本地U盘备份。人脑记不住这些节点于是AnyPS5接管了“记忆”的部分。4.2 U盘备份流程与AnyPS5的登记机制实际操作一点都不高科技在PS5上进入“设定 已保存的数据和游戏/应用设定 已保存的数据(PS5) 复制到USB驱动器”选择游戏复制。手动操作完毕之后在AnyPS5的Web面板里点一下“登记备份”填上游戏、时间、路径和备注后台自动计算该游戏的备份数并记录下来。为了让登记成本降到最低我在面板上预置了最近常玩的游戏列表点两下就能完成登记。同时我会在这个模块里存一条md5用来做备份文件的完整性校验。虽然U盘备份的文件结构没法直接逐文件比对但我能确认每次备份的文件清单在体积和数量上是否合理能提前发现某些游戏备份异常变小的情况。这里有一个从实际操作中得来的经验备份时建议把U盘插在主机背面接口部分前接口在数据传输过程中受供电波动会影响写入。我遇到过两次备份完成后文件列表显示不全后来固定使用后置接口问题消失。4.3 快照管理与恢复演练只有备份没有恢复演练等于白备份。我第一次做恢复测试是在某个游戏更新后突然无限报错当时把U盘插回去通过系统设置把存档复制回主机问题立刻缓解。从那次起我每两个月会挑一个不常玩的游戏做一次“备份-恢复-删除备份”演练AnyPS5里记录每次演练的结果。快照管理在数据库侧就是一套简单的版本列表同一游戏的最新三条备份会被标记为“keep”更早的可以选择清理。界面直接展示“当前备份数 / 最近备份时间 / 下次建议备份时间”一眼看明白。游戏最近备份备份数备注Elden Ring2024-05-123通关前最终版Baldurs Gate 32024-04-282二周目准备GT72024-06-011版本更新前备份这种不用猜的感觉真的很踏实。5. 游戏价格监控模块自己写爬虫踩过的那些细节价格监控可能是AnyPS5里最“现成工具很多”的领域但我还是选择自己写原因是现成工具都没法满足“多区域商店历史价格趋势自定义提醒阈值”这三个需求同时出现。5.1 为什么不用现成价格站市面上成熟的价格历史站数据覆盖确实广但很多站点更新不及时特别是折扣刚开始的前半小时页面价格已经变了站内数据还停留在原价。而我想在打折开始当天就收到提醒这个频率要求很多现成站做不到。另外我需要把价格跟自己的游戏库关联。一个游戏是否想买取决于它是不是在我的“愿望单”里而这个愿望单的数据其实已经躺在AnyPS5的游戏库里。自建模块可以直接做本地关联不需要在两个平台之间来回导数据。5.2 商店页面抓取与价格解析我选用了各个商店的公开网页版价格页面作为数据源因为它们不需要登录价格字段也在页面里直接渲染。PS商店页面结构比较稳定我用Playwright加载页面后等待特定价格节点出现再提取文本并转成数字。给一个简化版的抓取思路async def fetch_price(store_url): async with async_playwright() as p: browser await p.chromium.launch() page await browser.new_page() await page.goto(store_url, timeout30000) await page.wait_for_selector(.price-display__price, timeout10000) price_text await page.inner_text(.price-display__price) price parse_price(price_text) # 去掉货币符号和尾字 await browser.close() return price这个流程看起来很顺但真实环境比这残酷。不同区域商店的页面结构不完全一样港服的价格节点样式跟欧美服不同有的区域页面不展示折扣倒计时只展示最终价。我的办法是把区域差异配置化写了一个很长的区域配置表每个区域对应一组选择器和货币单位。5.3 定时任务的触发与“礼貌抓取”策略价格监控跑的是每日定时任务我会在上午和晚上各抓一次。为了避免给商店服务器压力每个请求之间至少间隔5秒并对同一个游戏的同一天抓取结果做了缓存如果今天已经抓到价格且页面没有明显的折扣标记变化就直接复用昨天的记录节省请求量。这里踩过一个典型坑商店有促销时页面里可能出现“折扣价”和“原始价”两个数字解析器如果只取第一个可能误把原价当作当前价导致提醒触发条件完全失效。我的解决方案是同时抓取两个字段只有当折扣价字段存在时才认为是促销状态否则一律视为原价。另一个坑是货币单位。欧美服常用美元、欧元港服是港币日服是日元我记录时全部保留原始币种换算成自己熟悉的币种放在展示层做避免汇率波动污染历史数据。6. 外设兼容性清单与游玩习惯统计从数据库到可用面板外设和习惯统计这两个功能其实都不复杂但它们是让AnyPS5从“工具集”变成“个人数据中心”的关键。一个只有价格和存档记录的工具用起来冷冷清清的加上外设和习惯数据之后才真正有了“给我自己用”的感觉。6.1 外设记录的字段设计我手头的外设类型很杂所以字段特意设计得宽松。核心字段包括外设名称、类型、连接方式、固件版本、兼容状态、备注、关联游戏列表。兼容状态我分三级完美兼容、部分兼容、不兼容。部分兼容这一级最有价值比如我的某第三方方向盘在GT7里力反馈正常但在《WRC》里按键映射错乱这些细节如果不记录几个月后再次接上时又得重新试一遍。我还给每个外设加了“最后验证日期”字段。有些外设的固件升级之后兼容表现会变化。我会在每次验证后更新这个字段面板上自动排序出“很久没验证的设备”提醒我该插上试试了。6.2 游玩习惯统计的维度游玩习惯数据统计依赖于前面的OCR模块。每次扫描截图我都会得到一张“什么时间、玩什么游戏、大概进度多少”的记录表。虽然它不是精确的分钟级统计但足够回答这些粗粒度问题我最近一个月玩得最多的类型是什么哪个时间段最容易开始新游戏我的烂尾率到底有多高我把这些维度做成几个简单聚合查询按月份统计各游戏出现次数按游戏状态统计数量分布按游戏类型统计游玩时长占比数据来自这个游戏的总截图数而非真实时长必须承认基于截图的统计无法做到100%准确但它的优势在于完全离线、完全隐私也不依赖任何官方数据接口。我宁愿查阅五天后修正的统计也不愿上传数据换一个实时的报表。6.3 Web面板的交互设计面板我用的是FastAPIJinja2Chart.js没有引入太重的后台模板。首页是汇总卡片游戏总数、玩过的比例、最近备份时间、今天的价格提醒数。第二页是游戏库表格支持按状态、来源、标签筛选。第三页是价格趋势点进某个游戏能看到历史价格曲线。第四页是外设清单按兼容状态排序。数据交互有一个需要注意的点游戏名排序在SQLite里默认按ASCII中文游戏名排序会乱。我最后在查询层用了自定义排序函数按拼音首字母排序显示效果立刻正常了。这种小细节很琐碎但直接决定了面板到底好不好用。7. 从开发到日常使用AnyPS5实际跑了三个月后的心得项目从开始到稳定运行差不多花了三个月。前一个月在写OCR和价格模块后两个月是持续调优。现在它已经成了我打开电脑后第一个启动的本地服务这里说点真实的使用体会。7.1 使用频率最高的功能排第一的是备份提醒不是价格监控。这其实超出我预期。价格监控虽然方便但真正触发购买的次数并不多反而是每月两次的备份提醒帮我避免了好几次“更新版本前忘记备份”的险情。排第二的是游戏库标签整理。因为OCR模块能自动把新截图识别进库我不用手动维护游戏库状态反而成了最精确的一份“游戏台账”。我甚至把PS Plus会免游戏单独打了标签每年到期前能快速看到哪些会免游戏还没玩优先补进度。排第三才是价格监控。它的价值不在于省钱多少而在于消除“怕买贵”的焦虑。看到某个游戏的历史价格曲线后我自然就知道该在什么价位入手。7.2 最大的意外收获游戏习惯的可视化我之前以为自己是个偏重单机RPG的玩家统计结果打脸动作冒险和竞速类占了近六成RPG反而很少。这个发现让我开始认真思考自己真正喜欢什么类型的游戏也直接影响了我后来的购买决策。另外一个收获是OCR扫描让我的截图库变得可搜索。之前拍了几千张截图基本永远不会再去翻现在它们被识别、归类、打上游戏标签偶尔查一个奖杯截图一秒钟就能找到。这个体验远好于在相册里手动翻页。7.3 还想继续做下去的方向AnyPS5目前的方案是单用户本地工具我会继续迭代的方向有三个。第一是多人协作家里有不止一台PS5的情况把备份登记和游戏库同步起来第二是更精细的OCR模型针对PS5系统UI字体做微调减少别名表维护量第三是把提醒做成桌面通知和手机推送不再依赖打开面板才能看到。但说实话写到最后我更想说的不是功能清单而是一种感觉。PS5只是一台游戏机但围绕它的数据、习惯和记忆是我自己的数字生活的一部分。AnyPS5帮我做的不过是把这一部分生活从混乱变清晰。每解决一个小需求数据库里就多一张可靠的表这种一点点把问题收进抽屉里的踏实感可能才是这个项目真正让我上瘾的地方。