最近游戏库里的3A大作一个比一个能吃硬盘前阵子看着《最终幻想》重制版、《GT赛车》新作和几个常驻的联机游戏主机自带的667GB实际可用空间直接见了底。那几天我每天开机第一件事就是纠结到底删哪个删了以后想玩怎么办下载又要等多久这种存储焦虑想必不少PS5玩家都有过。后来我想明白了与其每次靠感觉做决定不如做一个把玩家高频需求统一收拢起来的小工具于是就有了AnyPS5——一个本地的、数据自持的PS5辅助管理工具主要解决存储空间分析、游戏库整理、奖杯进度管理、主机健康检查这四件事。如果你也是PS5玩家或者正打算用Python给自己写点效率工具这篇项目总结应该对你有用。我不打算把它写成一份自吹自擂的项目演示而是把从需求分析、功能拆解到踩坑修复的完整过程都摊开来讲包括每个功能为什么这么设计、哪些数据源是现实可用的、哪些地方最容易翻车。整个项目没有碰任何系统权限不需要改装主机也不需要额外付费服务所有代码跑在电脑上数据都由玩家自己提供或授权导入。1. 项目的起点当2TB硬盘也装不下我的游戏库最早这个项目只有一个功能原型就是存储空间管理但越做越发现玩家的痛点从来不是单一维度的。存储焦虑只是表面背后其实是游戏库信息不透明你到底有多少游戏哪些装了没玩哪些玩了一半哪些已经白金了这些信息分散在主机界面、手机App、记忆和Excel表格里没有任何一个工具把它们串起来。1.1 三个日常痛点逼我动手先说存储。PS5的系统存储页面能看到每个游戏占多少空间但只能一个一个看没法排序也没有历史记录。我想知道过去半年我到底玩过哪些游戏系统不告诉我我想知道如果必须删三个游戏删哪三个损失最小系统也不告诉我。这一切只能靠玩家自己记。再说游戏库。数字版、光盘版、会免游戏、二档会员游戏混在一起主机界面的筛选功能够用但跨时间、跨类型地做统计就很弱。比如我买过多少款单人剧情游戏哪一年买得最多哪个类型囤得最多从来不玩这些问题的答案明明都在数据里却没有一个入口能看到。最后是奖杯进度。补完一个老游戏的奖杯需要反复对照当前进度、剩余目标、稀有度官方App的奖杯页只能说能用但离好用有距离。我想要的是一份可以按稀有度排序、按缺口筛选、能告诉我再拿三个铜杯就能升到下一等级的清单。这三个痛点单独看都不算大问题但凑在一起就很烦人我决定用代码一次性解决。1.2 为什么叫AnyPS5以及项目的形态名字里的Any有两层意思。第一层是任何玩家工具的使用门槛要低不能要求人人会写代码第二层是任何场景希望能把玩家日常高频操作一网打尽而不是做一个只有极客才用的命令行工具。当然理想和现实有差距。主机平台没有开放通用编程接口我不能直接读取系统数据所以AnyPS5最终做成了一个跑在电脑上的本地工具用Python编写通过玩家本人提供的截图、导出文件、公开页面快照、手动维护的数据表这四个来源获取信息。所有数据都保存在本地不需要注册账号不依赖服务器。这个形态可能不够酷但它足够干净、安全、可控。我给自己定了一条线AnyPS5只做管理玩家自己的数据不碰主机系统文件不涉及任何验证规避不做任何灰色操作。这条线从项目第一天就划死了后续所有功能决策都和它有关。2. 先定边界什么能做什么坚决不做开发工具类项目最怕的是没有边界什么都想做最后什么都做不成。AnyPS5立项的时候列了一个不做清单后来证明这份清单帮我省掉了至少90%的麻烦。2.1 三个不做帮我少了90%的麻烦第一不做任何系统层面的读取和修改。不碰主机系统分区不碰存档文件不碰系统固件。这意味着AnyPS5永远不会导致主机变砖也不会因为系统更新而失效。代价是我只能拿到玩家看得见的数据比如截图、页面文本。第二不做任何账号敏感信息的采集。不要求玩家输入主机账号密码不抓取未授权的个人数据不保存任何凭据。所有涉及在线信息的操作只针对玩家自己公开的内容或者玩家手动导出的本地文件。第三不做任何加速绕过解锁类功能。网络上这类需求很旺盛但往往是灰色地带而且和辅助管理的定位格格不入。AnyPS5的价值是帮玩家把已有的东西管好而不是去突破什么限制。这三条边界确立之后项目的技术选型也跟着清晰了不需要破解任何协议不需要逆向主机的通信流程只需要处理好本地文件解析、OCR识别、CSV数据管理和简单的数据可视化。2.2 数据从哪里来四条公开可行的路径既然不碰系统内部就得把公开数据源研究透。我实际验证过下面四条路径在现实场景里都是走通的数据用途来源路径获取方式更新频率存储空间分析主机系统设置中的存储页面截图玩家手动截图通过工具扫描识别每季度一次或空间告急时游戏库清单主机商店的已购列表 / 手机App的游戏库页面玩家手动导出页面文本或维护CSV每季度一次奖杯进度公开的奖杯展示页面玩家提供自己公开页面的页面快照每月一次游玩记录主机使用情况概览部分可从通知记录获取玩家手动补充或由CSV维护每周一次有人可能会说这四种方式都离不开手动太原始了。但事实上对个人工具而言半自动往往是稳定性和隐私性的最佳平衡点。我宁可每季度花十分钟截图导入也不想为了全自动去碰不该碰的接口。主流程做完之后我把整个项目拆成四个功能模块存储管家、游戏库整理、奖杯进度助手、主机体检。每个模块都可以独立使用也可以共享同一套CSV数据。接下来挑几个核心模块讲讲实现思路和踩坑过程。3. 存储管家模块从删哪个到有数据支撑地删这个模块是AnyPS5最早成型、也是使用频率最高的部分。它的核心任务只有一个把主机存储空间的截图变成带排序、带评分、带建议的删除清单。3.1 OCR识别截图识别游戏名与体积实现逻辑非常简单玩家在主机系统设置里打开存储页面对游戏列表逐屏截图然后把图片丢给AnyPS5工具调用本地OCR引擎识别出每一行的游戏名称和占用体积整理成结构化数据。实际操作中为了让识别率足够高我做了两个约束。第一是截图规范建议在主机系统界面把列表滚动到最顶部保持每行文字在同一水平位置避免游戏名称被图标遮挡分辨率尽量保持系统默认不需要特意调高。第二是识别策略游戏名和体积要分开识别体积单位统一转换成GB方便后续排序和计算。import json import re def parse_storage_line(raw_line: str): # raw_line 示例: 最终幻想 VII 重生 84.32 GB # 先提取体积数字和单位 pattern r([\d.])\s*(GB|MB)$ match re.search(pattern, raw_line.strip()) if not match: return None size_value float(match.group(1)) unit match.group(2) if unit MB: size_value size_value / 1024 # 剩余部分是游戏名 game_name raw_line.strip()[:match.start()].strip() return {name: game_name, size_gb: round(size_value, 2)}识别结果会统一写进一个名为storage_scan.json的文件里里面包含游戏名、体积、扫描时间和当时的剩余空间。多扫几次之后就自然有了历史数据可以画出一条存储水位曲线。3.2 体积排行榜与删除建议分有了结构化数据接下来的问题就是怎么帮玩家做删除决策我设计了一个删除优先级评分分数越低越优先删。评分公式如下基础分50分已通关且不追求全收集的游戏直接给到50分未通关的给30分仍在新游戏阶段一个月内购买的给10分。游玩活跃度0到30分以距离上次游玩的天数为指标越久没玩分数越高。超过90天给满分30分30天内给5分以下。体积修正0到20分体积越大的游戏删除后释放空间越明显给予额外加分。100GB以上给20分50到100GB给15分20到50GB给10分20GB以下给5分。联机依赖扣分项最高-20分如果游戏是常驻联机游戏比如每天都会上线做任务的类型扣20分表示系统不建议删除。这套评分规则本质上是把删哪个损失最小这个模糊问题量化。我见过不少玩家做删除决定时只看体积结果删了一个正在玩的游戏过两天又连夜下载回来白白浪费时间。有了评分之后决策就变成了一句话优先删通关后再没碰过、体积又大、和联机朋友无关的游戏。3.3 实测效果一次扫描省下200GB模块上线后的第一次全量扫描是在某个周末当时主机剩余空间只剩41GB。我按连续三屏截图导入OCR识别出35款游戏总占用约526GB其中体积最大的一款赛车游戏占了96GB上次游玩时间是132天前评分高达92分。系统给出的删除建议是按评分从高到低删掉六款游戏合计释放约219GB。实际操作中我没有一次全删而是先删了评分最高、体积最大的三款释放了约140GB剩下的等需要时再说。这个流程如果用人工来做光翻列表就要十几分钟而且很容易纠结。AnyPS5把整个过程压缩到了五分钟以内截图、导入、看推荐、动手删。这里有个比较重要的心得OCR不是100%准确的所以工具在更新删除建议前会显示识别置信度低置信度的行会被标出来让玩家人工确认。千万别图省事跳过这步我遇到过把游戏名GT赛车7识别成GT豪华7的情况要不是复查了一轮删游戏的时候就找不到对应条目了。4. 游戏库与奖杯模块让烂尾和补完都变得可视化存储模块解决的是空间不够怎么办游戏库和奖杯模块解决的则是我到底有哪些游戏、它们玩到什么程度。这两个模块共享同一份游戏主数据表是整个AnyPS5的数据中枢。4.1 游戏库CSV的维护技巧游戏库数据落在一个名为games.csv的文件里每行一款游戏字段包括字段名示例说明game_idFF7REBIRTH唯一标识建议用英文缩写title最终幻想VII 重生游戏中文名genre剧情RPG一级类型标签release_year2024发售年份source数字版/光盘版/会免获取渠道status通关/烂尾/进行中/未开始/白金当前进度状态last_played2025-01-12最近游玩日期size_gb84.32最近一次扫描到的体积notes二周目Hard难度备注这个表是整个项目里唯一需要玩家动手维护的地方也是大多数玩家觉得麻烦的环节。为了降低维护成本我做了两件事一是提供import_from_ocr()函数可以直接把存储扫描结果中的游戏名和体积导入自动填充title和size_gb两个字段二是把status字段做成了下拉式约束只允许填几个固定值避免打了玩了这类模糊词。我自己的维护频率是每季度一次加上每次通关后顺手改一下status。不需要做到实时因为游戏库的信息不像存储空间那样容易告急几周的延迟完全可以接受。4.2 奖杯进度抓取与稀缺度排序奖杯数据来自玩家公开的奖杯展示页面。AnyPS5不做账号登录只要求玩家把自己公开页面的内容保存为本地快照然后解析其中的奖杯列表和完成百分比。解析结果写入trophies.csv字段包括游戏名称、稀有度等级铜/银/金/白金、是否已获得、全球获得率、最后更新时间。拿到这张表之后最有用的功能是缺口清单比如你某个游戏还差3个铜杯、1个银杯就能白金清单会直接告诉你差什么、在哪个章节/模式拿、全球获得率是多少。按稀有度排序之后低获得率的奖杯会排在最前面因为它往往是全游戏最费时的一个点也是补完计划里最应该提前做功课的部分。我还做了一个白金距离指数其实就是缺失奖杯数量和估算耗时的加权用来解决要不要开这个坑的选择困难。比如一款通关了但对白金没有执念的游戏如果它的白金距离指数超过80意味着还要投入十几个小时且包含多个高难度杯系统会建议先别急着刷留着以后想挑战再说。4.3 两个数据源如何串起来游戏库CSV和奖杯CSV通过游戏名称关联。听起来简单实际上有坑因为这两个来源的名称写法经常不一样游戏库用的是中文名奖杯页面用的是英文名或缩写。解决方案是在game_id上做主键两个表都维护这个字段。名称匹配只用于导入时自动提示可能属于同一款游戏确认后由玩家指定对应的game_id。def guess_game_id(title: str, games_table) - str | None: # 优先精确匹配中文名其次去掉特殊字符后匹配最后按缩写匹配 candidates [] for row in games_table: if row[title] title: return row[game_id] normalized_title re.sub(r[\s:\-], , row[title]) if re.sub(r[\s:\-], , title) normalized_title: candidates.append(row[game_id]) return candidates[0] if len(candidates) 1 else None这个半自动关联的设计让AnyPS5在数据录入时要多花一点人工确认的时间但换来了极好的稳定性不会出现两套数据互相打架的情况。5. 开发踩坑记录小工具也有大教训AnyPS5本身不算复杂项目但麻雀虽小五脏俱全从编码、OCR、时区处理到文件编码全都踩过一遍。这里挑三个影响最大的坑分享。5.1 坑一OCR对简体中文游戏名识别率惨不忍睹第一版测试的时候OCR引擎对英文游戏名的识别率大概在95%以上但切到简体中文游戏名之后直线下降尤其是带VII重生Remake这种中英混排的名字经常把中文识别成错别字把罗马数字识别成乱码。排查之后发现问题不在OCR引擎本身而在预处理。主机系统界面使用的字体偏细而且列表行有半透明底层截图放大后文字边缘有大量锯齿。解决办法是先用图像处理库把截图转成灰度图做二值化处理再放大两倍后再交给OCR引擎。这个简单的预处理把中文游戏名的识别准确率从不到70%提升到了90%以上。另外我还加了一个游戏名词典作为兜底从主机商店页面整理了一份热门游戏名称列表OCR结果先和词典做模糊匹配匹配不上才靠纯文本识别。这样就算识别出错也能通过词典纠正回来。5.2 坑二时区让最近游玩天数算错一天删除建议评分里用到距离上次游玩日期的天数这个数据来自游戏库CSV的last_played字段。第一次写完我拿自己的数据测发现有些游戏明明昨天还在玩计算出来的天数却是2天。根因是时区错乱CSV里存的是本地日期CST程序里转成了UTC时间再和当天UTC时间相减导致昨天下午直接变成了前天多算了一天。这种问题在存储管理里不致命但会导致评分偏差如果体感不细心很容易被忽略。修复方案是CSV里的日期字段一律只存日期不存时间所有计算都基于日期字符串不做时区转换。具体来说就是date.fromisoformat()之后只比较date对象不交叉datetime。5.3 坑三Excel打开CSV时中文乱码但程序内正常这个问题特别经典。CSV文件用Python写出来的时候默认是UTF-8编码程序自己读没问题但玩家收到数据表之后往往用Excel打开一打开满屏乱码。Excel对UTF-8无BOM的CSV默认按本地编码解析中文自然就废了。解决办法是在写CSV时加上UTF-8 BOM头with open(games.csv, w, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow([game_id, title, genre])utf-8-sig写出来的文件带BOMExcel能正确识别程序员用Python读的时候又不会受到BOM影响。这个修复只有一行但属于不做就迟早会被用户问询的典型问题。5.4 结构设计与扩展取舍AnyPS5的代码结构被刻意设计成功能模块 数据层两大块。功能模块负责界面交互和业务逻辑数据层负责所有CSV和JSON的读写。这样做的好处是以后不管是想加Web界面还是把数据迁移到SQLite都不用改动核心逻辑。扩展性方面我做了两个取舍。第一暂时不做多用户支持因为个人工具的数据模型都是单机单用户的提前设计多用户会让复杂度暴增第二不做云同步数据留在本地反而省心真有同步需求可以自己放进网盘目录。这两个决定让整个项目始终保持周末能维护的体量。6. 后续可以怎么玩给想复刻的人留的作业AnyPS5做到当前版本对我来说已经解决了三分钟判断删什么游戏一眼看清游戏库结构快速定位白金缺口这三个核心问题。如果你也想复刻一个类似的个人工具有几个方向非常值得继续做。6.1 给存储模块加历史趋势每次扫描结果都会被记录那么画出一张主机剩余空间 vs 游戏库总体积的时间趋势图就是顺手的事。你甚至可以做简单的线性预测按最近三个月的涨势估算多久之后空间会再次告急提前决定是把旧的会免游戏清掉还是直接加一块外置硬盘。6.2 奖杯目标的缺口清单再进一步现在的奖杯缺口清单只做到了列出缺失奖杯和稀有度进一步可以做成补完路线图按奖杯之间的前置关系排序先给出一周目标再给出一个月目标。这个方向有点类似任务管理工具核心是维护一张奖杯依赖关系表工程量不小但做出来会很有成就感也会让白金列表不再只是挂在天花板上的装饰。6.3 新闻聚合与发售日历提醒游戏库CSV里有类型和关注度字段可以拿来做一个简单的发售日历提醒订阅公开的官方新闻RSS或页面改动一旦关注的系列有新作发售日就推送到本地通知。这个功能和主机数据无关完全独立作为个人学习试水的第二个项目也不错。如果你打算照着这个思路写我最想提醒你的是先花一个晚上把数据模型定清楚把游戏名游戏ID状态字段这些基础表设计好再动手写功能。我在0.1版本的时候就是建模没想清楚游戏名一会儿用中文一会儿用英文后面被迫重写了一遍导入逻辑。数据模型是这种个人工具的地基地基歪了上层功能再漂亮也会天天返工。