Chrome浏览器缓存查看与导出工具:解析Cache_Data并回放离线资源

发布时间:2026/10/14 0:43:57
Chrome浏览器缓存查看与导出工具:解析Cache_Data并回放离线资源
简介Chrome浏览器缓存查看与导出小工具是一款面向开发者、网页调试人员及普通用户的轻量级Windows小工具用于读取和导出Chrome本地缓存可浏览缓存中的图片、HTML文档、CSS样式表、JavaScript脚本等资源类型解决手动查找缓存目录繁琐、难以提取原始文件的问题适用于前端调试、离线素材提取等场景。压缩包共6个文件约17KB主要包含Python脚本、TXT说明文档、HTM中文说明、CHM帮助手册以及项目配置文件其中脚本可协助批量处理缓存项手册内容完整便于离线查阅说明文档则帮助不同基础的用户快速上手。目前已有42人学习下载。工具启动后自动扫描默认缓存路径列表清晰显示每条缓存记录的URL、内容类型、大小和最后访问时间选中目标后按F4快捷键即可自定义路径导出原始文件。配套中文说明与帮助手册能有效规避非官方汉化版兼容性差、容易出现乱码的痛点建议以原版英文界面配合中文文档使用免安装、绿色单文件运行整体轻巧实用尤其适合需要快速分析浏览器缓存机制的开发者和普通用户。1. 缓存查看与导出把 Chrome 的“黑匣子”打开变成自己的离线资产做前端调试或者临时想从浏览器里捞点素材的人应该都遇过这类场景线上某个接口突然 500但刚才明明加载过一份完整数据浏览器没刷新时页面好好的或者想给一个在线文档截图做留存右键另存只能存下 HTML 外壳真正的图片和样式却被浏览器锁在自己窝里。这个时候你会很自然地想到 Chrome 缓存目录打开一看却是一堆f_xxxxxx数字命名的文件加上一个index既不知道哪个文件对应哪个 URL也复制不出来能直接用的资源。这个标题里的「Chrome浏览器缓存查看与导出小工具含中文说明」解决的正是这个具体问题把 Chrome 在磁盘上落地的缓存数据从二进制垃圾变成带 URL 映射、可导出、可回放的资源集合。它不是网络请求抓包工具也不做网页下载器它的核心价值是把缓存文件的存储格式讲明白并给出一套能真正导出资源的操作路径。适合需要快速抢救离线资源的前端开发者、做浏览器取证分析的安全从业者以及想搞懂缓存目录结构的普通用户。下面按我实际落地的顺序从数据结构讲到导出脚本再讲踩坑和验证方法。2. 先看清数据长什么样Chrome 缓存的目录结构与自带的查看通道2.1 两代目录结构——别拿旧工具套新版本Chrome 的磁盘缓存经历过两次比较大的目录组织变化。你自己打开用户数据目录看会发现有Cache、Code Cache、CacheStorage、GPUCache等好几个带“Cache”字样的文件夹。这之间有什么区别需要先分清楚否则导出时连找文件的位置都定位不对。最早期版本用的是Cache目录下的data_0、data_1、data_2三个数据块文件加index索引文件的结构。缓存条目以固定格式写入数据块Chrome 通过索引文件定位条目在数据块中的偏移量。这种结构最麻烦的地方在于条目是变长的而且会被分块存储一个完整资源可能横跨两个数据块。想解析它得先读索引文件再按偏移组合数据纯手工提取非常痛苦。现在 Windows 和 Linux 桌面版 Chrome 基本不会在这种结构里写入新数据了但如果你手里有一份很老的用户数据目录备份还是会遇到。新版本 Chrome 的 HTTP 缓存默认拆成了Cache_Data下的f_前缀文件加index文件。每个f_文件就是一个完整缓存条目文件名是自动递增的十六进制序号里面按顺序记录着缓存条目头部包含 URL、时间戳、请求头、响应头等元数据、然后是真正的响应体数据。这种结构对导出工具来说友好得多文件名本身没有含义但文件内部带着完整的 URL 信息和资源数据解析就变成了一个“读文件头 → 找 URL → 提取 body”的过程。另外还有两类容易被误认的缓存Code Cache存的是 V8 编译后的字节码不是原始资源GPUCache是 GPU 着色器缓存。这两类不参与 HTTP 资源回放导出工具不用去碰它们。而你用 Service Worker 的 Cache API 存下的资源会进CacheStorage目录它下面按 origin 分目录每个目录里还有子目录结构又不一样这个我放到后面兼容逻辑里讲。2.2 用 Chrome 自带能力做第一层查看不用写代码也能确认资源在不在写导出脚本之前先用 Chrome 自带的能力确认目标资源确实还躺在缓存里能省掉很多无效解析。老版本有个chrome://cache页面可以直接列出缓存条目但这个入口在新版本里已经移除了。当前版本我常用的办法是走开发者工具里的 Network 面板存档。操作路径是打开准备排查的页面F12 切到 Network 面板右键任意请求选择 “Save all as HAR with content”Chrome 会把当前页面已加载的所有响应连同响应体完整导出成一个.har文件。这个文件是 JSON 格式里面每个条目都带request.url和response.content.text字段等于 Chrome 官方给了一条“查看缓存内容”的捷径。它的局限是只能导出当前页面上下文中已经加载过的请求页面刷新后消失的缓存条目它看不到也不能按磁盘文件角度去遍历全部缓存。在浏览器地址栏输入 chrome://version # 复制 个人资料路径 一栏的值这个路径就是用户数据目录 # Windows 通常形如C:\Users\用户名\AppData\Local\Google\Chrome\User Data\Default拿到路径后在文件管理器里直接进入Default\Cache\Cache_Data就能看到index和一堆f_文件。此时双击f_文件系统会提示你选择打开方式千万别选浏览器打开最好用十六进制编辑器查看。文件开头几个字节是固定的格式标记头部里能直接看到 URL 字符串——通过搜索文本片段可以粗略确认某个资源是否存在于磁盘缓存里。这一步能帮你判断到底是“资源根本没缓存”还是“缓存在但导出脚本没解析对”。2.3 自研导出工具的前置认知必须有 URL 映射才有导出价值很多人在这一步卡住的原因是尝试直接把f_文件复制出来改名为.png或.js发现文件能打开但内容不对头部多出一段乱码。原因很简单——缓存条目文件是“元数据 响应体”的复合格式直接改后缀等于把元数据也当成资源内容了。所以导出工具的核心逻辑不是复制文件而是解析头部、找到响应体起始位置、截取有效数据。一段典型的f_文件结构大致是开头 12 字节是元数据块长度信息接着是 URL 字符串以\0结尾然后是若干行 HTTP 请求头、响应头空一行后是真正的 body 数据。导出工具要做的就是从文件头开始先读 URL再按空行分割找到头部结束位置再根据头部里声明的长度字段决定 body 的截止位置。新版本里响应体可能直接挨着头部末尾也可能有额外的长度字段所以写解析脚本时对两种可能性都要做容错。3. 导出器最小实现从磁盘缓存文件到可回放资源3.1 先写一个针对新版 Cache_Data 的朴素导出脚本常见做法是用 Python 写一个批量提取器原理并不复杂遍历目录下所有f_开头的文件按字节流读取解析出 URL再找到 body 起始偏移把 body 内容写成本地文件。下面这个脚本是我习惯的样板实现去掉了无关的第三方依赖只用了标准库。import os import re import urllib.parse CACHE_DIR rC:\Users\你的用户名\AppData\Local\Google\Chrome\User Data\Default\Cache\Cache_Data OUTPUT_DIR rD:\chrome_cache_export def parse_cache_file(filepath): with open(filepath, rb) as f: data f.read() # 新版 Chrome 缓存条目头部固定以 0x1 开头后面跟元数据长度 url_start data.find(bhttp, 0, 256) if url_start -1: return None, None, None # URL 以 \0 结尾提取 URL 字符串 url_end data.find(b\x00, url_start) if url_end -1: return None, None, None url data[url_start:url_end].decode(utf-8, errorsignore) # 在 URL 之后是请求头/响应头头部和 body 之间隔一个空行 \r\n\r\n header_end data.find(b\r\n\r\n, url_end) if header_end -1: # 有些缓存记录用 \n\n 分隔兼容处理 header_end data.find(b\n\n, url_end) if header_end -1: return None, None, None body_start header_end 4 # 跳过 \r\n\r\n return url, data, body_start def safe_filename(url): # 把 URL 转成安全的本地文件名同时保留扩展名 path urllib.parse.urlparse(url).path if not path or path.endswith(/): name index.html else: name os.path.basename(path) name re.sub(r[^a-zA-Z0-9._-], _, name) return name def main(): os.makedirs(OUTPUT_DIR, exist_okTrue) count 0 for filename in os.listdir(CACHE_DIR): if not filename.startswith(f_): continue filepath os.path.join(CACHE_DIR, filename) url, data, body_start parse_cache_file(filepath) if not url: continue name safe_filename(url) # 同名资源加前缀避免覆盖 out_path os.path.join(OUTPUT_DIR, f{count:05d}_{name}) with open(out_path, wb) as out: out.write(data[body_start:]) print(f{url} - {out_path}) count 1 print(f共导出 {count} 个资源)逻辑说明parse_cache_file返回三个值——URL、完整文件数据、body 起始偏移。url_start的查找范围限制在前 256 字节是假设头部元数据不会过长如果 URL 没有出现在前 256 字节直接跳过。safe_filename取 URL 的路径部分作为文件名并保留扩展名这样导出的图片和脚本能直接按扩展名识别。body 起始位置的计算基于\r\n\r\n空行分割这是 HTTP 报文的标准分隔方式遇到只用了\n\n的缓存记录也能兼容跳过。参数说明CACHE_DIR是自己的 Chrome 用户数据缓存路径路径里如果有中文用户名注意 Python 源文件的编码声明OUTPUT_DIR是导出目录脚本运行前建议确认该目录存在或由脚本自动创建避免写文件时报错。文件名加count前缀是为了避免不同 URL 解析出相同文件名时互相覆盖同时保留数字前缀可以让导出结果的顺序和扫描顺序一致便于回查。3.2 兼容 CacheStorage 目录和旧版 data 块文件上面脚本只覆盖了最常用的Cache_Data路径。实际使用中你还会遇到两种情况一种是通过 Service Worker 的 Cache API 缓存的资源它们存放在CacheStorage目录下组织方式是按 origin 建目录origin 目录下还有一层随机命名的子目录里面同样有f_文件格式和Cache_Data下基本一致另一种是老旧的data_0/1/2数据块格式。对于CacheStorage做法是扫描时多走一层目录递归。递归逻辑可以概括为如果一级目录下有f_文件按原逻辑处理否则继续向下层扫描。每次进入子目录就重置计数前缀导出到独立的子目录中保持 origin 隔离。旧版data_x块文件的解析单独说明一下它是定长的 16KB 块链结构每个缓存条目由若干块链接而成。块头里的 next 指针指向下一个块编号块头还带 CRC 校验。这种格式最省事的处理方式是放弃自研解析直接用一个老版浏览器打开这个用户目录让 Chrome 自己重新索引这些数据块并触发旧的缓存迁移机制或者干脆做在线加载让资源重新验证。从成本考量不建议为 data_x 写完整解析器——它已经退场多年为存量数据维护一个复杂的链式解析器回报太低。CacheStorage 下的目录示例 CacheStorage\origin-hash\随机目录名\f_000001 # 其中 origin-hash 是按 origin 计算的固定前缀 # 随机目录名 是每次初始化时生成的随机串没有业务含义把这两层逻辑合并进主脚本时核心改动是让目录遍历变成递归式的for root, dirs, files in os.walk(CACHE_DIR)替代单层os.listdir然后在循环里判断f_前缀。注意别把Code Cache目录混进来简单做法是在遍历时显式排除名称包含Code Cache和GPUCache的目录。4. 导出过程中的 5 个踩坑与排查思路4.1 时间戳全是 0 或 1970解析错字段别慌现象想按时间排序导出结果时发现所有条目时间戳都是 0或者统一是 1970-01-01。原因Chrome 缓存条目头部的时间戳不是 Unix 时间戳更不是本地化日期字符串它是相对于系统启动时间的微秒计数偏移。如果你直接按固定偏移量取了字段读出来的数值要么落在 0 附近要么被解释成错误的历史日期。解决把时间戳换算成“系统启动时间 偏移量”来还原真实时间。具体做法是在打开浏览器前先记录psutil.boot_time()或操作系统的开机时间然后从缓存条目头部读出偏移值两者相加才是资源被缓存的时间点。如果只是作定位用途建议不要过度依赖时间排序——文件名递增顺序本身就是缓存写入顺序两者基本正相关。4.2 导出的 PNG 能打开但图片花屏CSS 首部有乱码现象脚本导出的图片文件用看图软件能打开但画面花屏、颜色错乱CSS/JS 文件开头有一段明显不属于源代码的二进制垃圾。原因body 起始偏移算错了。parse_cache_file里用data.find(b\r\n\r\n, url_end)不一定准确——部分 Chrome 版本在 URL 之后、HTTP 头部之前还固定写入了一段额外字段比如请求优先级、缓存验证信息这些字段结束时的分隔符正好也是\r\n\r\n。脚本从 URL 之后找第一个空行如果遇到的空行是元数据区内部的分隔符body_start 就会落在真正 body 之前把后面的缓存管理字段一并写入了导出文件。解决把查找基准往后挪用响应头特征判断。在缓存条目的头部区响应头一定以HTTP/1.1 200 OK或类似状态行开头可以先用data.find(bHTTP/1., url_end)锁定状态行位置再从状态行之后找第一个空行这样定位的 body 才是真正响应体。我改造后的脚本先搜HTTP/前缀找不到再回退到简单空行查找。4.3 导出的资源打开后 404URL 是百分号编码的文件路径没解码现象某些 URL 里带%20、%E4%B8%AD%E6%96%87这类编码导出到本地后文件名保留了百分号但用本地服务器访问时路径对应不上。原因Chrome 缓存条目里存的是原始请求 URL没有经过解码。脚本里用urllib.parse.urlparse(url).path拿到的 path 依旧是编码形态转成文件名后自然带着%字符。而本地回放时浏览器会自动解码两边的路径就不一致了。解决生成文件名时调用urllib.parse.unquote先做一次百分比解码再过滤非法文件名字符。需要保留编码版本的场景反而少见——导出目标基本都是给人类看的资源文件解码后的名称更直观。注意中文字符在 Windows 下存文件名没问题但在写代码时要用encodingutf-8打开输出文件。4.4 磁盘缓存里找不到大体积资源现象页面里有个 50MB 的视频或一个超大 PDF断网后 Chrome 能播放但磁盘缓存目录里就是找不到对应文件。原因Chrome 对体积较大的响应会策略性跳过磁盘写入内存缓存兜底。磁盘缓存有容量上限常见默认值 300MB 左右且对超过阈值的大文件可能直接标记为不缓存。所以你导出的是磁盘视角的资源集合它天然不完整。解决这个限制靠自研脚本无法绕过。如果你确实需要大资源应该在它还在网络层时用 HAR 导出Save all as HAR with content 会把内存缓存中的内容也一并写入或者提前用 DevTools 的 Network 面板做目标文件单独保存。做取证分析时最好把磁盘导出和 HAR 导出两个结果交叉合并避免遗漏。4.5 浏览器占用导致文件读取失败现象脚本运行到一半报PermissionError或文件读取返回空。原因导出过程中 Chrome 仍然在运行某些f_文件正好处于被读取状态Windows 下文件锁会让 Python 读不到内容。这属于竞态问题不是脚本逻辑错误。解决最省心的做法是先完全退出 Chrome 再执行导出脚本。有的开发者坚持边开浏览器边导我通常会把用户数据目录整体先复制一份到临时目录再在副本上扫描——既不影响当前浏览也绕开文件锁。复制目录时注意Cache目录可能很大复制后记得核对副本里的文件数量是否和源目录一致。5. 导出后的回放方案把资源文件变回可访问的站点结构5.1 按 URL 路径重建本地静态服务器目录导出的一堆文件都是扁平结构文件名带了数字前缀直接双击看单个文件没问题但做整体回放时容易出现路径引用断裂——HTML 里引用的css/main.css在扁平目录里根本不存在。常见做法是用导出时保留的 URL 路径重建目录层级。我一般会让脚本多做一个步骤在解析时记录每个导出的文件对应的 URL path并把这些信息写入一个manifest.json。回放时按 manifest 把扁平文件软链接到对应的目录结构里再起一个静态文件服务器指向这个根目录。python -m http.server 8080 --directory D:\chrome_cache_export\site_root # 在浏览器访问 http://localhost:8080/index.html # 就可以按原始站点的相对路径访问导出的资源原理说明http.server是 Python 自带的最简静态服务器--directory参数指定站点根目录。用软链接组织目录的好处是磁盘上文件仍只有一份不重复占用空间。如果导出时 URL 路径里带查询参数?id123脚本应忽略 query 部分再生成路径否则目录结构里会出现无效字符。5.2 用 HTML 索引页做离线资源清单目录方式适合重建完整站点但当你只是想快速确认“这堆缓存里到底有哪些资源”时一个可视化的索引页更高效。做法是让导出脚本在最后生成一份index.html内容是从 manifest 生成的资源表格每一行展示 URL、导出文件名、文件大小、资源类型并为图片和视频类资源直接内嵌预览。!-- 索引页核心逻辑从 manifest.json 读取资源列表并渲染 -- script fetch(manifest.json) .then(r r.json()) .then(list { const ul document.getElementById(resource-list); list.filter(item item.contentType.startsWith(image/)).forEach(item { const li document.createElement(li); const img document.createElement(img); img.src item.fileName; li.appendChild(img); li.appendChild(document.createTextNode( ${item.url})); ul.appendChild(li); }); }); /script代码说明这里的逻辑是从 manifest 过滤出所有图片类型资源并逐个渲染预览图。实际使用时可以扩展成按类型分组HTML/CSS/JS 资源展示成一个可点击打开的链接列表图片视频类直接内嵌字体类展示文件大小和格式。索引页配合本地服务器访问比一个个打开文件检查有安全感得多。这一个附加步骤在平时不显眼但遇到导出几百个资源、需要快速辨别哪些文件对应哪些页面时能直接省掉反复看图猜来源的时间。我的个人习惯是导出后先打开索引页扫一遍资源清单确认 URL 映射没乱、时间戳范围合理再决定是归档还是继续做渲染回放。现在每次给缓存目录做批量提取都会顺手把 manifest 和索引页一起生成后面再查什么资源直接从清单里定位不再重复扫盘。希望帮到你。本文还有配套的精品资源点击获取