| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- """
- 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()
|