全部技能 / 基础工具与工作流 / download-gemini-images
基础工具与工作流 · daymade/claude-code-skills

download-gemini-images

Download, export, save, or package images from a Google Gemini conversation/chat/app page, especially uploaded images or generated image previews visible in a Gemini thread. Use when the task needs logged-in Chrome/Gemini state, opening Gemini image lightboxes, downloading the larger displayed image files, renaming them in order, and producing a ZIP archive.

风险提醒:黄色 · 留意使用AI 侦查报告
作者 daymadeGitHub daymade/claude-code-skills ↗Stars 1385许可 MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)commit d5c4678cb5
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

核心前提是「借用用户已登录的 Chrome」而不是自己开浏览器:Gemini 会话通常要现成的 Google 登录态,所以第一步就把无痕新浏览器排除掉。

download-gemini-images/SKILL.md
1. Use the Chrome plugin, not a fresh unauthenticated browser, because Gemini conversations usually require the user's existing Google session.
注:这里在做什么:把「认证」这件事外包给用户的实时浏览器会话;skill 自己不接触任何账号/密码输入,只是驱动一个已经登录的标签页。

脚本通过宿主的 browser 能力列出所有已开标签页,用 URL 或(gemini.google.com + 标题含 Gemini)双条件匹配,再 user.claimTab 认领;匹配不到直接报错,绝不自行新开页面。

download-gemini-images/scripts/download_gemini_images.mjs
return await browser.user.claimTab(matches[0]);
注:同一函数上文是 matches.length === 0 时 throw new Error(`No open Gemini tab matched ${tabUrlIncludes}`),下文 claimGeminiTab 被 downloadGeminiImagesFromChrome 用作 options.tab 缺省值。认领式(而非新建)保证了作用对象就是用户眼前那一个会话。

必须先点开 lightbox 才能拿到大图:Gemini 只在预览态暴露更大的 blob: 图,所以脚本逐张点按钮后轮询等待 img.currentSrc 变成新的 blob:、且 naturalWidth/naturalHeight 都大于 0。

download-gemini-images/scripts/download_gemini_images.mjs
info.src.startsWith("blob:") &&
注:这是 waitForSelectedImage 的判定核心(同一 if 里还要求 naturalWidth > 0 && naturalHeight > 0 && (!previousSrc || info.src !== previousSrc)),配合 previousBlobSrc 记录上一张,防止「点了按钮但图还没换」时抓到旧图。SKILL.md 概述把它讲成设计动机:『Prefer the lightbox image over thumbnail/page-preview assets because Gemini often exposes a larger `blob:` image only after the user opens a preview.』

下载落盘后再按序改名:先从 blob URL 尾部用 UUID 正则抽出标识,到 Downloads 目录按 uuid+扩展名(或最新 mtime 且 mtime>=开始时间-2s)定位刚下载的文件,再 copyFile 成 image_01.jpg 这种两位序号名。

download-gemini-images/scripts/download_gemini_images.mjs
const outputName = `image_${pad2(index + 1)}${normalizeExtension(downloadedPath)}`;
注:上一行是 const uuid = imageInfo.src.match(/\/([0-9a-f-]{36})$/i)?.[1] || null;,waitForDownloadedMedia 优先按 `${uuid}${ext}` 精确命中、否则退化为「目录里最新的媒体文件」。有序命名是 skill 的显式交付契约(SKILL.md:『use the script's ordered names: `image_01.jpg`, `image_02.jpg`, etc.』)。

打包是一个独立 bash 脚本,且自带双重自证:先 zip -T 测完整性,再比对目录内 image_NN 数量与压缩包内条目数量,不等即非零退出。

download-gemini-images/scripts/package_images.sh
echo "zip image count mismatch: directory=$image_count zip=$zip_count" >&2
注:上文 image_count 来自 find -maxdepth 1 -type f 搭配 image_*.jpg|jpeg|png|webp;zip_count 来自 zipinfo -1 管道再 grep 正则 '/image_[0-9][0-9]\.(jpg|jpeg|png|webp)$'。SKILL.md 把这一步写成:『The packaging script creates the ZIP, runs `zip -T`, and verifies that the archive contains the same ordered image files as the directory.』

循环体里有两道防错位护栏:每张先按 ESC 关掉可能的旧灯箱,且每次重新统计按钮数量,一旦与初始数量不符立刻抛错——避免页面异步加载后第 N 个按钮指向另一张图。

download-gemini-images/scripts/download_gemini_images.mjs
throw new Error(`Gemini image button count changed from ${initialCount} to ${currentCount}`);
注:这里在做什么:把「同一页面上按钮序号的稳定性」当成不变量来断言。同样在循环外,expectedCount 若由用户给出则 initialCount !== expectedCount 直接抛错,防止下少了/下多了还当作成功。

降级路径走宿主标签页的 pageAssets 能力按 URL 前缀筛图并打包,且明确承认画质退化并要求告知用户。

download-gemini-images/SKILL.md
var uploaded = inventory.assets.filter(a => a.kind === "image" && a.url.includes("lh3.googleusercontent.com/gg/"));
注:紧随其后的行是:『This usually captures 512px preview JPEGs, not the larger lightbox files. Tell the user when this fallback is used.』——降级不是静默发生,而是被要求写进回复里。

每次运行都留下 manifest.json 记录页标题、页 URL、输出目录与每张图的序号/原始下载路径/像素尺寸,交付物可回溯。

download-gemini-images/scripts/download_gemini_images.mjs
const manifestPath = path.join(outputDir, "manifest.json");
注:同段用 writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8") 落盘,并把 pageTitle / pageUrl / outputDir / imageCount / files 一并返回给调用方。

2核心能力

01在用户已登录的 Chrome 里认领 Gemini 标签页(不新建、不使用无痕)
02逐张打开 lightbox 抓取大图(blob: 预览图而非缩略图)
03按线程顺序重命名为 image_01.jpg / image_02.jpg …
04expectedCount 严格计数校验(用户报了几张就必须是几张)
05打包成 ZIP 并做完整性 + 条目数双重校验
06画质退化时的 pageAssets 兜底路径(并明确告知用户用的是低分辨率)
07运行时产出 manifest.json,交付可审计
08明确的隐私边界:不碰 cookie / localStorage / 密码 / 会话存储

3外部依赖

类型依赖
api宿主 Chrome 插件能力 browser(user.openTabs / user.claimTab)
api宿主 node_repl(homeDir、在同一 JS 会话里 import 脚本)
api宿主标签页 pageAssets 能力(降级路径)
networkgemini.google.com(默认标签页匹配串,实际流量由用户浏览器发出)
networklh3.googleusercontent.com(降级路径按该 CDN 前缀筛图片资产)
clizip / zipinfo(打包与条目校验)
clifind / wc / grep(图片计数与条目数比对)
api宿主 CUA 按键能力 tab.cua.keypress(发 ESC 关灯箱)

4风险提醒 风险提醒:黄色 · 留意使用

风险提醒:黄色 · 留意使用
  • 高权限上下文:在用户已登录 Google 的浏览器里真实点击与下载。 — claimTab 借用登录态;误操作(点错按钮、页面结构变化)作用对象是用户自己的账号会话。SKILL.md 的边界只有一句『Do not inspect browser cookies...』,其对「不要在会话里顺手做别的事」的约束弱于 github-ops 那类显式 blast-radius 契约。
  • 写盘位置固定且不清理:下载目录会堆积中间文件。 — 浏览器先落到 ~/Downloads/<uuid>.<ext>,脚本只 copyFile 不删除,SKILL.md 也未要求清理;多次运行会留下多份副本。ZIP 输出默认写在输出目录旁(<image_dir>.zip),写前 rm -f 同名文件。
  • 页面结构强耦合:选择器是硬编码 aria-label。 — DEFAULT_BUTTON_SELECTOR = 'button[aria-label="Show the uploaded image in a lightbox"]';Gemini 改文案即整体失效。失效方式是可接受的(抛错退出),但意味着维护依赖上游 DOM。
  • pageTitle/pageUrl 原文回流给模型。 — manifest.json 会存页面标题与 URL 并返回给调用方;Gemini 会话标题由对话内容决定,属弱注入面(模型若把标题当指令即中招),实际风险低。
  • 依赖宿主实现细节,跨宿主不可移植。 — browser.user.openTabs/claimTab、tab.cua.keypress、tab.playwright.locator、pageAssets 都是宿主私有 API;换宿主需改写脚本。
风险提醒:黄色,留意使用。按统一分档:网络外发对象只有两个且完全可预期(gemini.google.com 与 lh3.googleusercontent.com,均为 Google 自家域名),无第三方回调、无 API key、无埋点;本地写入面明确(下载目录 + 输出目录 + manifest + ZIP)。之所以不是蓝色:实际执行环境是用户已登录 Google 的 Chrome 会话,脚本会认领真实标签页并在其上点击、下载,属于高权限上下文;之所以不是橙色:它没有读取 cookie/环境变量/密钥库等任何凭证载体(SKILL.md 明文禁止),也不依赖第三方远程包或镜像——借的是宿主提供的浏览器能力而非凭证数据本身。附带说明:若现场误用(例如用户手动先开了无痕窗)脚本会因匹配不到标签页而直接报错退出,不会退化到匿名路径。

5第二遍独立确认

  • [ok] 外部依赖:宿主 browser 能力真实存在(不是推测) — mjs:124 return await browser.user.claimTab(matches[0]); 与 mjs:118 await browser.user.openTabs() 是真实调用点;SKILL.md 第 3 步要求 import 后在 node_repl 里跑,两处对得上。
  • [ok] 外部依赖:zip / zipinfo / find / wc 调用点 — package_images.sh 内 zip -qr、zip -T、zipinfo -1、find ... -iname 'image_*.jpg'、wc -l 均实读命中,脚本是真的会跑这些二进制,不是文档口号。
  • [ok] 网络端点数量:只有 Google 两个域名 — 全目录域名级检索仅 gemini.google.com 与 lh3.googleusercontent.com;无第三方 API、无 webhook、无 telemetry。注意 mjs 中出现的 blob: 是页面内对象 URL,不是网络端点。
  • [discrepancy] 凭证面:是否真的不碰 cookie(找反例) — 〔第二遍标注:措辞需精确〕反例检索结果:源码中确实没有任何 document.cookie / chrome.cookies / localStorage 访问,SKILL.md 也明文禁止。但『不读凭证』不等于『不使用凭证态』——claimTab 借用的正是用户已登录会话。第一遍若把此条简单写成「无凭证接触」会掩盖真实权限面,故在 credential_reads 中改写为『无凭证文件读取,但运行于凭证态上下文』。
  • [ok] 『绝不自开新浏览器』是否成立 — claimGeminiTab 在 matches.length === 0 时抛错(No open Gemini tab matched),downloadGeminiImagesFromChrome 中 options.tab 与 claimGeminiTab 二选一,无 puppeteer.launch 之类的建窗代码,与 SKILL.md『claim the already-open Gemini tab』一致。
  • [ok] 输出完整性校验是否真做了(非文档承诺) — package_images.sh: zip -T 有实调用,且 image_count 与 zip_count 不等时 exit 1;mjs 侧有 initialCount 校验、按钮数不变量、selectedCount !== 1 抛错——三道都是代码而非描述。
  • [ok] expectedCount 语义是否被夸大 — description 未提 expectedCount;SKILL.md 只说『Set `expectedCount` only when the user gave a specific count』,代码里 initialCount !== expectedCount 时抛错,与文档一致,无越界承诺。
  • [ok] 元数据(license/stars/last_push) — 取自同 repo 同 commit 的池内既有值(与 auto-repo-setup.json / bilibili-source.json 一致:MIT、1385★、2026-09-09T12:33:29Z);本次未联网复核 gh。

6结论

  • 认证方式干净:借用户实时 Chrome 会话,不索取也不读取任何凭证载体。
  • 抓取路径选对了:直奔 lightbox 大图,而不是缩略图或页面预览资产。
  • 顺序与数量都有硬不变量,抓错比抓不到更早暴露。
  • 打包产物自带完整性自证,交付可核对。
  • 降级不静默:低分辨率兜底会写进回复,用户不会误以为拿到原图。
  • 适合:适合「我已经在 Chrome 里开着这段 Gemini 对话,把里面的图给我扒下来打包」这类场景:登录态现成、图数量明确(可传 expectedCount)、需要按线程顺序的连续编号文件。也适合需要一次性拿到整套图片再离线处理(做 PPT、做素材库)的用户。
    不适合:不适合未登录/无开放标签页的场景(脚本会直接报错,不会自己登录);不适合要求原图最高分辨率且拒绝降级的场景(兜底路径只给 512px 预览);不适合批量抓取多个会话或全账号图库(每次只认领一个标签页);不适合无 Chrome 插件宿主;也不适合需要清理下载目录副产物的洁癖环境。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-15
    方式 A · 人下载镜像包下载 download-gemini-images.tar.gz
    sha256: 2c75fce87072dc44…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit d5c4678cb5;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库daymade / daymade/claude-code-skills
    Stars1385
    最近推送2026-09-09
    本 skill commitd5c4678cb5
    许可MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)
    本站信息
    收录日期2026-09-06
    分类基础工具与工作流
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近