Python | EXIF日付別アルバム+タグフィルタ付きHTMLギャラリー

Python
スポンサーリンク

いいですね!ここまでの要素を全部組み合わせて、「フォルダ内の画像をサムネイル化 → EXIF日付ごとにアルバム分け → HTMLギャラリー生成 → タグでフィルタ表示」 する実践例をまとめます。


概要

  1. Pillow で画像を読み込み、サムネイルを生成
  2. EXIFの撮影日 を取得して日付ごとにアルバム分け
  3. タグ情報data-tags 属性に埋め込み
  4. JavaScript でタグフィルタを実装

Pythonスクリプト例

from pathlib import Path
from PIL import Image, ExifTags
import html, shutil
from datetime import datetime

# 入出力ディレクトリ
src_dir = Path.home() / "Pictures" / "gallery_src"
out_dir = Path.home() / "Pictures" / "gallery_out"
thumb_dir = out_dir / "thumbs"
img_copy_dir = out_dir / "images"

out_dir.mkdir(exist_ok=True)
thumb_dir.mkdir(exist_ok=True)
img_copy_dir.mkdir(exist_ok=True)

# EXIFタグ辞書
EXIF_TAGS = {v: k for k, v in ExifTags.TAGS.items()}

def get_taken_date(img_path: Path) -> str:
    """EXIFから撮影日を取得。なければ更新日"""
    try:
        with Image.open(img_path) as img:
            exif = img._getexif()
            if exif:
                dto_tag = EXIF_TAGS.get("DateTimeOriginal")
                if dto_tag in exif:
                    dt = datetime.strptime(exif[dto_tag], "%Y:%m:%d %H:%M:%S")
                    return dt.date().isoformat()
    except Exception:
        pass
    return datetime.fromtimestamp(img_path.stat().st_mtime).date().isoformat()

def make_thumbnail(src: Path, dst: Path, size=(240,240)):
    with Image.open(src) as img:
        img.thumbnail(size)
        if img.mode != "RGB":
            img = img.convert("RGB")
        dst.parent.mkdir(parents=True, exist_ok=True)
        img.save(dst, "JPEG", quality=85)

# 仮のタグ付けルール(例: ファイル名に含まれる単語でタグ付け)
def assign_tags(filename: str) -> list[str]:
    tags = []
    if "trip" in filename.lower():
        tags.append("旅行")
    if "family" in filename.lower():
        tags.append("家族")
    if "food" in filename.lower():
        tags.append("食べ物")
    return tags or ["その他"]

# 画像処理
items = []
for img_path in src_dir.glob("*.*"):
    if img_path.suffix.lower() not in [".jpg",".jpeg",".png"]:
        continue

    date = get_taken_date(img_path)
    thumb_path = thumb_dir / f"{img_path.stem}_thumb.jpg"
    make_thumbnail(img_path, thumb_path)

    # 元画像コピー
    copy_path = img_copy_dir / img_path.name
    if not copy_path.exists():
        shutil.copy(img_path, copy_path)

    tags = assign_tags(img_path.name)
    items.append({
        "date": date,
        "full": f"images/{img_path.name}",
        "thumb": f"thumbs/{thumb_path.name}",
        "title": html.escape(img_path.stem),
        "tags": tags
    })

# HTML生成
html_path = out_dir / "index.html"
html_content = f"""<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>タグフィルタ付きギャラリー</title>
<style>
body {{ background:#111; color:#eee; font-family:sans-serif; }}
header {{ padding:16px; text-align:center; font-size:20px; }}
#filters {{ text-align:center; margin:12px; }}
#filters button {{ margin:4px; padding:6px 12px; }}
.grid {{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(160px,1fr));
  gap:12px; padding:12px;
}}
.card {{ background:#1b1b1b; border:1px solid #333; border-radius:8px; overflow:hidden; }}
.thumb {{ width:100%; height:auto; object-fit:cover; aspect-ratio:1/1; }}
.meta {{ padding:8px; font-size:12px; color:#bbb; }}
</style>
</head>
<body>
<header>タグフィルタ付きギャラリー</header>
<div id="filters">
  <button data-filter="all">すべて</button>
  <button data-filter="旅行">旅行</button>
  <button data-filter="家族">家族</button>
  <button data-filter="食べ物">食べ物</button>
  <button data-filter="その他">その他</button>
</div>
<main class="grid">
{"".join(f'''
  <div class="card" data-tags="{' '.join(item['tags'])}" data-date="{item['date']}">
    <a href="{item['full']}" target="_blank">
      <img class="thumb" src="{item['thumb']}" alt="{item['title']}" loading="lazy">
      <div class="meta">{item['title']}<br>{item['date']}<br>タグ: {', '.join(item['tags'])}</div>
    </a>
  </div>
''' for item in items)}
</main>
<script>
document.querySelectorAll('#filters button').forEach(btn => {{
  btn.addEventListener('click', () => {{
    const filter = btn.getAttribute('data-filter');
    document.querySelectorAll('.card').forEach(card => {{
      const tags = card.getAttribute('data-tags').split(" ");
      if (filter === "all" || tags.includes(filter)) {{
        card.style.display = "";
      }} else {{
        card.style.display = "none";
      }}
    }});
  }});
}});
</script>
</body>
</html>
"""
html_path.write_text(html_content, encoding="utf-8")
print("HTMLギャラリーを生成しました:", html_path)
Python

ポイント

  • EXIF日付でアルバム分け → data-date 属性に保持(将来的に日付フィルタも可能)
  • タグ付けはここでは「ファイル名に含まれる単語」で簡易的に実装
  • HTMLでは data-tags にタグを入れ、JavaScriptでフィルタ

拡張アイデア

  • 複数タグ選択(チェックボックスで「旅行+家族」など)
  • 検索ボックスで自由入力フィルタ
  • EXIFのカメラ機種やレンズ名をタグにする
  • 日付別アルバムページを自動生成して、トップページからリンク

✅ これで「サムネイル化」「EXIF日付」「タグフィルタ表示」を全部組み合わせたギャラリーが完成します。

Python
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました