App Store 图片校验 & 飞书图片替换
从 App Store 获取应用官方截图,与飞书表格中嵌入的市场图进行交叉验证,识别并替换错误图片。
触发条件
- 用户提到"核对图片"、"校验市场图"、"检查截图"、"App Store 截图"
- 用户说"图片对不对"、"市场图有问题"、"替换错误图片"
- 用户需要确保飞书表格中的产品截图与官方 App Store 一致
第一步:确认飞书表格(必须先做)
场景 A:用户提供了飞书表格链接
从链接中解析 spreadsheet_token 和 sheet_id。
飞书链接格式示例:
- https://xxx.feishu.cn/sheets/ABCD1234?sheet=EFGH5678
- spreadsheet_token = ABCD1234
- sheet_id = EFGH5678
解析后读取表格结构,确认以下列:
| 列 | 内容 | 说明 |
|---|---|---|
| B | 产品名称 | 用于展示和校验时的参照 |
| C | App ID | 查询 App Store 截图的关键列 |
| D | 市场图 | 嵌入图片的目标校验列 |
如果 D 列不是图片列而是文本/链接,本 skill 不适用。
场景 B:用户未提供飞书表格链接
必须先向用户索要飞书表格链接。本 skill 仅校验已有表格,不会自动创建新表。
> 禁止在未获得飞书表格链接的情况下继续执行后续步骤。
第二步:提取表格中的图片信息
2.1 获取单元格内容
lark-cli sheets +cells-get \
--spreadsheet-token <spreadsheet_token> \
--sheet-id <sheet_id> \
--range D2:D{last_row}
从返回的 JSON 中提取每行的嵌入图片 token。关键字段路径:
- sheets[0].data[0].rowValues[].values[].image.imageToken
- sheets[0].data[0].rowValues[].values[].image.width
- sheets[0].data[0].rowValues[].values[].image.height
2.2 下载嵌入图片
# 逐个下载到本地临时目录
lark-cli drive +download \
--token <image_token> \
--output /tmp/dimgs/row_{N}.jpg
同时从表格中提取每行的 App ID(C 列),用于后续查询。
第三步:获取 App Store 官方截图
3.1 查询应用信息
https://itunes.apple.com/lookup?id={app_id}&country=us
返回 JSON 格式:
import requests
def get_app_info(app_id):
url = f"https://itunes.apple.com/lookup?id={app_id}&country=us"
resp = requests.get(url)
data = resp.json()
if data['resultCount'] > 0:
result = data['results'][0]
return {
'name': result.get('trackName'),
'screenshots': result.get('screenshotUrls', []),
'ipad_screenshots': result.get('ipadScreenshotUrls', [])
}
return None
3.2 下载截图
从 screenshotUrls 数组中取前 4-5 张 iPhone 截图:
curl -s -o /tmp/appstore_shots/row_{N}_s{i}.jpg "{screenshot_url}"
> 注意:iTunes API 的截图 URL 可能包含 URL 编码的特殊字符(如 U0028 = (),curl 可直接使用,无需额外解码。
3.3 拼接截图为横向组合图
将多张 App Store 截图拼接为一张横向长图(匹配飞书市场图的常见展示格式):
from PIL import Image
def stitch_screenshots(image_paths, output_path, quality=95):
"""将多张截图横向拼接为一张图,统一高度"""
shots = [Image.open(p) for p in image_paths]
min_h = min(img.height for img in shots)
resized = []
total_w = 0
for img in shots:
ratio = min_h / img.height
new_w = int(img.width * ratio)
resized.append(img.resize((new_w, min_h), Image.LANCZOS))
total_w += new_w
result = Image.new('RGB', (total_w, min_h), (255, 255, 255))
x = 0
for img in resized:
result.paste(img, (x, 0))
x += img.width
result.save(output_path, "JPEG", quality=quality)
return output_path
第四步:图片交叉校验
4.1 校验方法
对比飞书表格中的市场图与 App Store 官方拼接图:
1. 视觉对比(最可靠)
- 查看 App Store 截图中的应用名称/Logo 是否与飞书表中的产品名一致
- 对比 UI 风格、功能界面截图是否匹配
- 对比颜色方案、布局结构是否一致
2. 对比方式
- 将飞书表中的市场图下载到本地
- 将 App Store 截图拼接图也存到本地
- 通过视觉检查对比两者(AI 视觉能力或人工)
4.2 判断标准
| 判定 | 条件 | 处理 |
|---|---|---|
| 匹配 | 表格图与 App Store 截图是同一产品 | 无需处理 |
| 匹配(品牌名差异) | UI 一致但品牌文字略有不同(如 "Clean Go" vs "Nova Cleaner") | 无需处理,这是开发者的正常营销行为 |
| 不匹配 | 表格图展示的是完全不同的应用(不同 UI、功能、开发商) | 替换为 App Store 官方截图 |
| 存疑 | 无法确定(截图差异大但可能只是版本更新) | 标记为需人工复查 |
> 关键原则:开发者可能在 App Store 截图中使用与商店列表不同的品牌名称,这属于正常现象。判断核心是「是否是同一款应用」,而不是「品牌文字是否完全一致」。
第五步:替换错误图片
5.1 对于确认不匹配的行
# 将拼接好的 App Store 官方截图上传并嵌入飞书单元格
lark-cli sheets +cells-set-image \
--spreadsheet-token <spreadsheet_token> \
--sheet-id <sheet_id> \
--range D{row} \
--image /tmp/appstore_shots/row_{N}_composite.jpg \
--name "AppName_market.jpg"
+cells-set-image 自动完成两步:
1. 上传图片到飞书 Drive
2. 将图片嵌入目标单元格(替代原有图片)
5.2 替换后验证
# 回读确认新图片已生效
lark-cli sheets +cells-get \
--spreadsheet-token <spreadsheet_token> \
--sheet-id <sheet_id> \
--range D{row}
确认返回的 imageToken 已更新,且 width/height 符合预期。
第六步:生成校验报告
报告应包含:
| 项目 | 数量 |
|---|---|
| 总校验行数 | N |
| 匹配(正确) | N |
| 不匹配(已替换) | N |
| 存疑(需人工复查) | N |
对每行给出判定和依据。对已替换的行列出新的 image_token。
第七步:清理
# 清理临时图片文件
rm -rf /tmp/dimgs /tmp/appstore_shots
关键注意事项
跨平台兼容性
本 skill 设计为在任何支持以下能力的 AI 编程助手中运行:
| 所需能力 | 用途 | 常见实现 |
|---|---|---|
| Shell 命令执行 | 运行 lark-cli、curl、Python | 各平台通用 |
| Python 运行时 + Pillow | 图片拼接、处理 | 各平台通用 |
飞书 CLI (lark-cli) |
飞书表格读写、图片管理 | 需提前安装并授权 |
| 视觉/图片对比能力 | 对比表格图与官方截图 | AI 内置视觉或人工 |
不使用任何平台专有工具或 Skill。所有命令均为跨平台通用 CLI。
App Store API 限制
- iTunes Search/Lookup API 无认证即可使用,但有速率限制
- 建议每次查询间隔 0.5-1 秒,避免被限流
country=us获取美区截图;如需其他地区修改country参数- 如果 API 返回空结果,检查 App ID 是否正确
图片对比难点
- 开发者品牌不一致:App Store 截图中的品牌名可能与商店列表名不同(正常现象)
- 截图版本差:表格中的图片可能是旧版截图,与当前 App Store 截图 UI 有差异
- 判断原则:以「是否是同一款应用」为核心判断标准,不以文字完全一致为准
- 存疑行宁可标记为人工复查,不要错误替换正确的图片
飞书图片操作
+cells-set-image会自动完成上传和嵌入两步- 支持 JPG 和 PNG 格式,建议 JPG 质量 90-95%
- 替换图片会覆盖原单元格中的图片,不影响单元格中其他内容(如文本)
- 图片上传后占用飞书 Drive 空间
批量处理效率
- 对于大量行(50+),建议分组处理:
1. 先批量下载所有行的 App Store 截图(API 查询 + curl 下载)
2. 再批量拼接组合图
3. 然后统一对比校验
4. 最后统一替换错误图片 - 修复时优先处理明确不匹配的行,存疑行汇总后人工决策
完整命令行速查
# === 飞书操作 ===
# 读取单元格内容(含图片信息)
lark-cli sheets +cells-get \
-t <spreadsheet_token> -s <sheet_id> -r D2:D55
# 下载飞书 Drive 中的图片
lark-cli drive +download \
--token <image_token> -o /tmp/dimgs/row_N.jpg
# 替换单元格中的图片
lark-cli sheets +cells-set-image \
-t <spreadsheet_token> -s <sheet_id> \
--range D22 \
--image /tmp/appstore_shots/row_22_composite.jpg \
--name "product_market.jpg"
# === App Store API ===
# 查询应用信息(含截图URL列表)
curl -s "https://itunes.apple.com/lookup?id=1234567890&country=us" | python3 -m json.tool
# 下载单张截图
curl -s -o /tmp/appstore_shots/row_N_s0.jpg "https://is1-ssl.mzstatic.com/..."
# === 图片拼接(Python) ===
python3 -c "
from PIL import Image
paths = ['/tmp/appstore_shots/row_22_s0.jpg', '/tmp/appstore_shots/row_22_s1.jpg']
shots = [Image.open(p) for p in paths]
min_h = min(img.height for img in shots)
total_w = sum(int(img.width * min_h / img.height) for img in shots)
result = Image.new('RGB', (total_w, min_h), (255,255,255))
x = 0
for img in shots:
w = int(img.width * min_h / img.height)
result.paste(img.resize((w, min_h), Image.LANCZOS), (x, 0))
x += w
result.save('/tmp/appstore_shots/row_22_composite.jpg', 'JPEG', quality=95)
print('Done:', total_w, 'x', min_h)
"
错误处理速查
| 错误 | 原因 | 解决 |
|---|---|---|
| App Store API 返回空 | App ID 无效或已下架 | 检查 App ID,或尝试 country=cn |
curl 下载截图超时 |
网络问题或 URL 失效 | 使用 curl --retry 3 --connect-timeout 10 |
+cells-get 无 imageToken |
该单元格没有嵌入图片 | 确认 D 列为图片列;检查 range 范围 |
+cells-set-image 失败 |
图片过大或格式不支持 | 转换为 JPG,确保宽高 < 5000px |
| 图片对比不确定 | UI 变化大 | 标记为「存疑」,不要强行替换 |
| PIL 拼接图片尺寸过大 | 截图数量多或分辨率高 | 降低拼接质量或减少截图张数 |