""" v2: 基于用户标注 (保留 7 条) + 新一轮搜索补充, 按"真实感 / 棚拍 / 写真"三方向分类。 读取两批 cache (旧的在 /Users/sunlit/Profile/.cache, 新的在 ./.cache), 合并图片到 images/, 输出 result.json。 注意: 重跑会覆盖外部追加的字段 (例如 method / index), 如要保留请改成 merge 模式。 """ import json import re from pathlib import Path import httpx OLD_CACHE = Path('/Users/sunlit/Profile/.cache/content_search') NEW_CACHE = Path('/Users/sunlit/Profile/analysis/ai-portrait-realism/.cache/content_search') OUT_DIR = Path('/Users/sunlit/Profile/analysis/ai-portrait-realism') IMG_DIR = OUT_DIR / 'images' IMG_DIR.mkdir(parents=True, exist_ok=True) MAX_IMAGES_PER_ITEM = 6 # (cache_dir, file, platform, index, slug, category, user_kept, user_comment, description) # user_kept: True 表示用户在 v1 标注里明确保留, False 表示 v2 新增 # user_comment: 用户在 v1 标注里给该条的备注 (无则空字符串) PICKS = [ # ═══════════════════════════════════════════════════════════ # 真实感 (网感) — iPhone 自拍 / 闪光灯 / 主动加瑕疵 / 平庸感 # ═══════════════════════════════════════════════════════════ (OLD_CACHE, 'apr-xhs-jimeng.json', 'xhs', 8, 'xhs-jimeng-iphone-selfie', 'realism', True, '', '⭐ 核心"网感" prompt: "iPhone 后置自拍 + 闪光灯 + 运动模糊 + 构图随意 + 角度尴尬 + 平凡无奇";' '把所有"完美/讲究"指令删掉, 主动加入瑕疵。'), (OLD_CACHE, 'apr-xhs-jimeng.json', 'xhs', 9, 'xhs-jimeng-real-beauty-prompt', 'realism', True, '', '与同款"日常自拍"风格 prompt 的另一版本(场景换为酒店房间), 验证模板可迁移到不同场景。'), (OLD_CACHE, 'apr-xhs-quaiwei.json', 'xhs', 10, 'xhs-anti-ai-influencer-formula', 'realism', True, '', '"消除 AI 感"配方: 网红/素人感博主 + 浴室/寝室/咖啡厅 + 前置摄像头/自然光/真实感 + 高冷+放荡不羁的矛盾感。'), (NEW_CACHE, 'apr-gzh-banana2.json', 'gzh', 7, 'gzh-banana2-rainy-girl', 'realism', False, '', 'Nano Banana 2 雨天少女 prompt: 完整结构化 prompt(主体/五官/姿势/服装/构图/光线), ' '"梦幻雨天 + 透过湿润伞面 + 略带忧郁" 的情绪+真实感混合。属于真实感里的"故事性瞬间"派。'), # ═══════════════════════════════════════════════════════════ # 棚拍 — 影棚 / 干净光线 / 商业肖像 / 影楼级精致 # ═══════════════════════════════════════════════════════════ (OLD_CACHE, 'apr-xhs-realhuman.json', 'xhs', 4, 'xhs-nanobanana-studio-cream', 'studio', True, '', 'Nano Banana 奶白棚拍 prompt: 含构图/主体/动作/环境/光线/镜头/质感全套描述; ' '"低姿态 + 干净光线", 强调"加入 8-12% 胶片颗粒/避免塑料皮"。'), (OLD_CACHE, 'apr-xhs-realhuman.json', 'xhs', 7, 'xhs-orchid-white-moonlight', 'studio', True, '也是棚拍类型的', '"白月光兰花人像"完整指令: 妆容 + 姿态 + 构图 + 道具 + 灯光分层描述, ' '低饱和度复古橄榄绿背景墙的棚拍布景, 东方氛围感。'), (NEW_CACHE, 'apr-gzh-banana2.json', 'gzh', 1, 'gzh-banana2-7-styles', 'studio', False, '', '实测 Nano Banana 7 种高级人物摄影写真模板 (黑白影棚/美式高管/韩系轻透 等), ' '每种风格都给完整 prompt, 是棚拍方向的最全模板库之一。'), (NEW_CACHE, 'apr-gzh-banana2.json', 'gzh', 2, 'gzh-banana2-dark-cinematic', 'studio', False, '', 'Nano Banana 2 高质感暗调电影感东亚女性人像: 4900+ 字超长结构化 prompt' '(核心主体/发型/皮肤/体态/服装/摄影参数全维度), 是"高级感+真实感"棚拍的标杆样本。'), # ═══════════════════════════════════════════════════════════ # 写真 — 摄影师作品级, 有审美/艺术气息/真实感, 棚拍与自拍之间 # ═══════════════════════════════════════════════════════════ (OLD_CACHE, 'apr-xhs-daily.json', 'xhs', 5, 'xhs-banana-pro-realhuman-pack', 'portrait_art', True, 'prompt在图片里,要注意提取', 'Banana Pro 真实感人像 prompt 包(10 张例图), 多风格/多场景的成片美学参考图库。'), (OLD_CACHE, 'apr-zhihu-realistic.json', 'zhihu', 18, 'zh-6-scenes-light-skin', 'portrait_art', True, '', '6 组场景的"光影适配 + 法医级皮肤纹理"复刻技巧, 用 Nano Banana Pro 出片;' '场景含日式庭院/复古客厅/画室侧窗等, 把"场景适配光影"这个被忽视的维度讲清楚。'), (NEW_CACHE, 'apr-gzh-jimeng.json', 'gzh', 5, 'gzh-jimeng-zimei-template-pack', 'portrait_art', False, '', '《我用AI给自己拍了套写真!》⭐ 607 likes 标杆型公众号文章, ' '14 张样片 + 万能提示词, 是"普通人 vs 影楼写真"的对照组, 写真方向最具传播力的样本之一。'), (NEW_CACHE, 'apr-gzh-jimeng.json', 'gzh', 8, 'gzh-jimeng-10-studio-styles', 'portrait_art', False, '', '"10 大影楼质感" — Nano Banana + 即梦, 5200+ 字, 75 张样片;' '横跨 10 种风格(高级摄影棚/复古港风/婚纱/古风等), 每种风格都给独立 prompt, 区分男女版本, 适合直接抄。'), (NEW_CACHE, 'apr-gzh-jimeng.json', 'gzh', 3, 'gzh-jimeng-mood-portrait', 'portrait_art', False, '', '即梦 4.0 高级人物肖像照 (附提示词), 摄影棚风格脸部特写 + 极简灰色渐变背景 + ' '黑白胶片颗粒 + 非居中构图 — 是"棚拍但有写真气质"的过渡范式。'), (NEW_CACHE, 'apr-gzh-banana2.json', 'gzh', 12, 'gzh-banana2-godlike-portrait', 'portrait_art', False, '', 'NanoBanana 神级人物摄影 ⭐ 771 likes 高人气, "顶级摄影肖像照"评测;' '生活照 → 高 bigger 肖像照, 是"作家级人像"思路的入门级范例。'), (NEW_CACHE, 'apr-gzh-banana2.json', 'gzh', 4, 'gzh-banana2-10-goddess-prompts', 'portrait_art', False, '', 'Nano Banana 10 组女神专属玩法 (高反差影棚人像/宅男卧室电脑等), ' '每组覆盖一个流行美学风格, 是"小红书爆款写真"的反向工程图鉴。'), (NEW_CACHE, 'apr-xhs-korean.json', 'xhs', 1, 'xhs-korean-tianmei-paobao', 'portrait_art', False, '', '韩系画报少女写真 ⭐ 1024 likes, 完整 prompt(发型/妆容/服装/道具/背景/姿势);' '"画报感"恰好是写真的核心 — 比手机自拍精致, 比影棚自然。'), (NEW_CACHE, 'apr-xhs-korean.json', 'xhs', 4, 'xhs-korean-8k-glass-skin', 'portrait_art', False, '', '8k 商拍超真实半身写真 ⭐ 1409 likes, K-Beauty 美学;' '"玻璃肌 + 自然雀斑 + 柔和工作室灯光" — 干净通透但保留真实瑕疵, 是用户描述的"有审美+真实感"的精确实例。'), (NEW_CACHE, 'apr-xhs-korean.json', 'xhs', 7, 'xhs-korean-white-oxygen', 'portrait_art', False, '', '⭐⭐ Nano Banana 白色氧气感写真 397 likes, ' '"干净又温柔 / 通透 / 亲近感" — 完整 prompt 描述了一字肩白色针织毛衣 + 微侧站立 + 柔光自然光线;' '最贴近用户对"写真"的描述: 不影楼/不随手自拍/有艺术气息。'), (NEW_CACHE, 'apr-xhs-korean.json', 'xhs', 10, 'xhs-korean-cool-aesthetic', 'portrait_art', False, '', '清冷氛围感韩系肖像写真 316 likes, "低饱和低对比的高调人像 + 冷暖反差(冷灰白调+蜜桃粉)";' '日韩系空气感的标准做法, 适合做静谧/诗意/略带脆弱的氛围写真。'), (NEW_CACHE, 'apr-xhs-travel.json', 'xhs', 7, 'xhs-travel-korean-wedding', 'portrait_art', False, '', 'Seedream 韩式海景婚纱写真大师指令 ⭐ 553 likes, 18 张样片;' '冷蓝调 + 电影质感 + 海面背景, 多人 + 单人构图全 cover, 旅拍/婚纱型写真模板。'), (NEW_CACHE, 'apr-xhs-outdoor.json', 'xhs', 5, 'xhs-outdoor-summer-film', 'portrait_art', False, '', '⭐ 0 成本逆光梦中情照 3683 likes, "夏日清新胶片写真" — ' '法式庭院 + 藤编椅 + 绣球花丛 + 午后侧逆光 + 暖调胶片颗粒, 9 张不同动作;' '是"户外/旅拍/胶片感"写真的标杆配方。'), (NEW_CACHE, 'apr-xhs-literary.json', 'xhs', 9, 'xhs-literary-vintage-film', 'portrait_art', False, '', '复古胶片氛围感少女 1520 likes, "旧报刊亭/橘粉花丛/虚焦背景拖影/重影/胶片颗粒纹理";' '画面带"故事感", 平衡"妖冶与甜美", 同时具备"网红网感"和"作品感" — 是写真方向最有"叙事性"的样本。'), (NEW_CACHE, 'apr-xhs-mood.json', 'xhs', 3, 'xhs-mood-eastern-classical', 'portrait_art', False, '', '岁序更迭: 东方古韵 AI 提示词技巧 ⭐⭐ 63410 likes 大热门; ' '不是直接给 prompt, 而是讲方法论 — Agfacolor 胶片色彩 / 高调影棚柔光 / 微缩布景手绘接景;' '适合做"东方写真"美学定调的参考。'), # ── 第 3 轮追加 (关键词: AI 故事感 / AI 怎么写真 / AI 氛围感 / AI ins风) ── (NEW_CACHE, 'apr-xhs-story.json', 'xhs', 5, 'xhs-story-iwai-shunji', 'portrait_art', False, '', '⭐⭐ 岩井俊二美学 AI 生图 prompt 1884 likes — "All About Lily Chou-Chou" 二创, ' '完整英文 prompt: 35mm film grain, soft focus, atmospheric haze, cool cyan-blue / warm golden hour backlight, ' 'strong lens flare, muted desaturated color palette, low angle shot, Japanese youth aesthetic. ' '是"日系作家级故事感写真"的标杆样本, 比一般氛围感写真更有作品感与情绪深度。'), (NEW_CACHE, 'apr-xhs-story.json', 'xhs', 10, 'xhs-story-hongkong-cafe', 'portrait_art', False, '', '⭐ 港风茶餐厅生活感写真口令 1366 likes — 完整 prompt(发型/妆容/服装/背景/光影) + 9 种动作姿势模板. ' '"复古胶片 + 港风 + 生活烟火气" 的精确 prompt, 是写真方向"生活感叙事"派的最强样本之一. ' '场景具象: 香港老式茶餐厅 / 旧海报 / 绿色铁皮餐桌 / 暖黄色调 / 明显胶片颗粒.'), (NEW_CACHE, 'apr-xhs-story.json', 'xhs', 3, 'xhs-story-karin-reference', 'portrait_art', False, '', 'AI 胶片摄影师 Karin. 的作品参考帖 2829 likes — body 短, 但提到了"日式胶片复古人像独特故事感"作为审美锚点. ' '可作为"作家级写真"赛道里的一个真实存在的 IP 案例(Karin), 用于对标 / 反向研究其 prompt 路径.'), (NEW_CACHE, 'apr-xhs-story.json', 'xhs', 9, 'xhs-story-grass-portrait', 'portrait_art', False, '', 'AI 户外草地写真样片 1815 likes — body 仅一句 "图片为 ai 创作", 但 8 张 AI 户外写真成片可作为美学参考. ' '配合岩井俊二/港风/Karin 一起看, 形成"故事感写真"成片库。'), ] # 去重检查 (按 cache_file + cache_index) def _check_dedup(): seen = {} for p in PICKS: key = (p[1], p[2], p[3]) # (cache_file, platform, index) if key in seen: raise ValueError(f'Duplicate pick: {key} -> {p[4]} clashes with {seen[key]}') seen[key] = p[4] _check_dedup() def safe_filename(s): return re.sub(r'[^\w\-.]', '_', s)[:80] def download(url, dest, client): if dest.exists(): return True try: r = client.get(url, follow_redirects=True, timeout=30.0) r.raise_for_status() dest.write_bytes(r.content) return True except Exception as e: print(f' [X] {url[:80]}... -> {e}') return False def ext_from_url(url): m = re.search(r'\.(jpg|jpeg|png|webp|gif)(\?|$)', url, re.IGNORECASE) return ('.' + m.group(1).lower()) if m else '.jpg' def main(): final = [] with httpx.Client(headers={ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' }) as client: for cache_dir, cache_file, platform, index, slug, category, user_kept, user_comment, description in PICKS: cache_path = cache_dir / cache_file if not cache_path.exists(): print(f'[!] missing {cache_path}') continue d = json.load(cache_path.open()) entry = d.get(f'search:{platform}') if not entry: continue posts = entry['posts'] if index < 1 or index > len(posts): continue post = posts[index - 1] title = post.get('title') or '' body = post.get('body_text') or '' url = post.get('link') or '' author = {'xhs': '小红书博主', 'zhihu': '知乎作者', 'gzh': '公众号作者', 'bili': 'B 站 UP'}.get(platform, platform) img_urls = [u for u in (post.get('images') or []) if u] local_paths = [] for i, img_url in enumerate(img_urls[:MAX_IMAGES_PER_ITEM], 1): ext = ext_from_url(img_url) fname = safe_filename(f'{slug}-{i:02d}{ext}') dest = IMG_DIR / fname if download(img_url, dest, client): local_paths.append(f'images/{fname}') print(f' [OK] {fname}') cover = local_paths[0] if local_paths else '' final.append({ 'category': category, 'user_kept': user_kept, 'user_comment': user_comment, 'description': description, 'cover': cover, 'title': title, 'author': author, 'body': body, 'images': local_paths, 'url': url, 'note': f'platform={platform} | cache_file={cache_file} | cache_index={index} | likes={post.get("like_count")}', }) tag = '[KEPT]' if user_kept else '[NEW] ' print(f'[done] {tag} [{category}] {slug}: {len(local_paths)} imgs') out_path = OUT_DIR / 'result.json' out_path.write_text( json.dumps(final, ensure_ascii=False, indent=2), encoding='utf-8', ) # 分类统计 from collections import Counter cat_counts = Counter(item['category'] for item in final) kept_count = sum(1 for it in final if it['user_kept']) new_count = len(final) - kept_count print(f'\n=== Wrote {len(final)} items to {out_path}') print(f' user_kept: {kept_count} | new: {new_count}') for cat, cnt in cat_counts.items(): kept_in_cat = sum(1 for it in final if it['category'] == cat and it['user_kept']) print(f' {cat}: {cnt} (kept={kept_in_cat}, new={cnt - kept_in_cat})') if __name__ == '__main__': main()