アイキャッチ画像を毎回Canvaで作るのに10分以上かけていませんか?PythonとPillowがあれば、記事タイトルを渡すだけで0円・数秒でアイキャッチを量産できます。月額課金の画像生成AIに頼らなくても、テキスト描画・背景デザイン・バッチ処理まで全部Pythonで完結します。この記事では、コピペですぐ動く実装コードと、実際の運用フローまで丁寧に解説しますね。
フェーズ1:なぜアイキャッチ自動化が必要か
アイキャッチ作成の現実的なコスト問題
ブログを継続的に運営していると、アイキャッチ画像の作成がじわじわと大きな負担になってきます。Canvaで1枚作るのに10〜15分かかるとして、月に20記事書けば単純計算で3〜5時間がアイキャッチ作業だけに消えていきます。しかもその作業は本質的な「記事を書く」「SEOを改善する」といった業務とは別腹のコストです。
では画像生成AI(Stable DiffusionのAPIやMidjourneyなど)に頼ればよいかというと、これもコスト面で悩ましいところがあります。月額課金のサービスは1枚あたりのコストが安く見えても、大量生成・商用利用・著作権の問題が複雑に絡んできます。個人ブロガーや中小規模のメディア運営者にとっては、月数千円〜数万円の課金が「割に合わない」と感じるケースも少なくありません。
さらに言えば、AIが生成した画像はサイトのブランドカラーやトンマナ(トーン&マナー:デザインの統一感)とズレることが多く、毎回プロンプトを調整する手間も発生します。結局「手作業と変わらないじゃないか」となりがちです。
Pillowで自動化できる範囲と限界
Pillow(Python Imaging Library のフォーク版。Pythonで画像を扱うための定番ライブラリ)を使えば、以下のことが完全無料でできます。
- ✅ 指定サイズのキャンバス生成(1200×630pxなどSNS最適サイズ)
- ✅ 背景色の塗りつぶし・グラデーション風の装飾
- ✅ 日本語フォントを使ったテキスト描画・自動折り返し・中央揃え
- ✅ アクセントラインやロゴのオーバーレイ
- ✅ カテゴリ別の配色切り替え
- ✅ CSVから複数タイトルを一括バッチ生成
- ✅ WebP形式への変換・ファイルサイズ最適化
一方で、Pillowだけでは難しいこともあります。写真素材を使ったリッチなデザイン、複雑なグラフィック合成、AIが生成するような「映え」感のある画像などは、Pillowの得意領域ではありません。ただし「シンプルで統一感のあるブランドアイキャッチ」という観点では、Pillowで作ったテキスト主体の画像は視認性が高く、むしろSEO的にも安定したクリック率を維持しやすいというメリットがあります。
「完璧なデザイン」を求めるのではなく「量産・統一・0円」を優先するなら、Pillowは最強のツールです。ハナはこのトレードオフを理解した上で、積極的に活用することをおすすめしています。
フェーズ2:Pillowの基礎とアイキャッチ設計の考え方
Pillowのインストールと主要クラスの役割
まずPillowをインストールしましょう。ターミナル(コマンドプロンプト)で以下を実行するだけです。
pip install Pillow
インストール後に使う主要クラスは3つです。それぞれの役割を頭に入れておくと、コードを読むときにぐっと理解が深まります。
| クラス名 | 役割 | 主な使い方 |
|---|---|---|
Image |
画像の生成・読み込み・保存 | Image.new() でキャンバス作成、img.save() で保存 |
ImageDraw |
図形・テキストの描画 | draw.rectangle() で矩形、draw.text() でテキスト描画 |
ImageFont |
フォントの読み込み | ImageFont.truetype() でTTF/TTCフォントを指定 |
この3クラスを組み合わせるだけで、アイキャッチ画像の生成に必要な処理はほぼすべてカバーできます。Pillowの詳細なAPIについては、Pythonで一気に画像編集できるPillowを使った画像処理のチートシート | ハシカケも参考になりますよ。
ブログ映えするアイキャッチの構成要素
アイキャッチ画像を設計する前に、「どんな要素が必要か」を整理しておきましょう。ブログやSNSシェアで映えるアイキャッチには、以下の要素が揃っていることが重要です。
推奨サイズ:1200×630px
これはOGP(Open Graph Protocol:SNSでシェアされたときに表示されるサムネイル画像の仕様)の推奨サイズです。TwitterカードやFacebook、LINEのシェアで正しくトリミングされずに表示されます。WordPressのアイキャッチとしても一般的なサイズです。
背景色の選び方
単色の背景でも十分に見栄えします。重要なのは「テキストとのコントラスト比」を確保すること。ダーク系の背景(濃紺・チャコール)に白テキストの組み合わせは視認性が高く、スマートフォンのサムネイル表示でも読みやすいです。カテゴリ別に背景色を変えると、読者がコンテンツを識別しやすくなるメリットもあります。
テキスト配置
タイトルテキストは画像の中央揃えが基本です。長いタイトルは自動で折り返し処理を入れ、2〜3行に収まるようにフォントサイズを調整します。上下にアクセントラインを入れるだけでデザインの完成度が大きく上がります。
日本語フォントの選び方
Pillowで日本語を表示するには、日本語対応のTTF/TTCフォントが必要です。おすすめはNoto Sans JP(Googleが提供する無償・商用利用可のフォント)です。太めのウェイト(Bold)を使うと、小さいサムネイルでも文字が潰れず読みやすくなります。システムフォント(WindowsならMeiryo、MacならHiragino)を使う方法もありますが、サーバー環境での動作を考えるとNoto Sansが最も安定しています。
Pillowを使った画像生成の基礎については、Pythonライブラリ Pillow で画像生成&保存する – dotTrailも合わせて読むと理解が深まります。
フェーズ3:アイキャッチ自動生成スクリプトの実装
ここからが本題です。実際に動くコードを順番に見ていきましょう。各ステップをそのまま実行すれば、手元の環境でアイキャッチ画像が生成できます。コードはすべてコピペOKです。
ステップ1:必要なライブラリのインストール
PillowとCSV処理に必要なパッケージをインストールします。CSVとPathlibはPython標準ライブラリに含まれるため追加インストール不要です。
pip install Pillow
ステップ2:日本語フォントの準備
Noto Sans JPをGoogle Fontsからダウンロードし、プロジェクトのfonts/ディレクトリに配置します。Ubuntu/Debianの場合は以下でも取得できます。
sudo apt install fonts-noto-cjk
# フォントパス例: /usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc
Windowsの場合はGoogle FontsのNoto Sans JPページからZIPをダウンロードし、解凍したTTFファイルをプロジェクト直下のfonts/フォルダに置いてください。フォルダ構成のイメージは以下のとおりです。
your-project/
├── fonts/
│ └── NotoSansJP-Bold.ttf
├── articles.csv
├── generate_eyecatch.py
└── output/ ← 生成画像の保存先(自動作成)
ステップ3:アイキャッチ画像を生成するコア関数
テキスト・背景色・フォントサイズを引数で受け取り、1200×630pxのPNG画像を生成して保存します。テキストは自動で折り返し・中央揃えされます。
from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
def generate_eyecatch(
title: str,
output_path: str,
bg_color: tuple = (30, 30, 30),
text_color: tuple = (255, 255, 255),
font_path: str = "fonts/NotoSansJP-Bold.ttf",
font_size: int = 72,
accent_color: tuple = (255, 80, 80),
width: int = 1200,
height: int = 630,
) -> None:
img = Image.new("RGB", (width, height), color=bg_color)
draw = ImageDraw.Draw(img)
# アクセントライン(上下)
line_h = 8
draw.rectangle([0, 0, width, line_h], fill=accent_color)
draw.rectangle([0, height - line_h, width, height], fill=accent_color)
font = ImageFont.truetype(font_path, font_size)
# テキスト折り返し処理
max_width = width - 120
words = list(title)
lines = []
current = ""
for char in words:
test = current + char
bbox = draw.textbbox((0, 0), test, font=font)
if bbox[2] - bbox[0] > max_width:
lines.append(current)
current = char
else:
current = test
if current:
lines.append(current)
# 行間・全体の高さを計算して中央配置
line_height = font_size + 16
total_text_height = line_height * len(lines)
y_start = (height - total_text_height) // 2
for i, line in enumerate(lines):
bbox = draw.textbbox((0, 0), line, font=font)
text_w = bbox[2] - bbox[0]
x = (width - text_w) // 2
y = y_start + i * line_height
# 影
draw.text((x + 3, y + 3), line, font=font, fill=(0, 0, 0, 120))
draw.text((x, y), line, font=font, fill=text_color)
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
img.save(output_path, "PNG")
print(f"[OK] {output_path}")
# 単体テスト実行
if __name__ == "__main__":
generate_eyecatch(
title="PythonのPillowでアイキャッチ画像をゼロ円自動生成する方法",
output_path="output/test_eyecatch.png",
bg_color=(20, 20, 40),
accent_color=(80, 160, 255),
)
このコードのポイントをいくつか補足しておきます。
- アクセントライン:
draw.rectangle()で上下に8pxの帯を描画しています。accent_colorを変えるだけでブランドカラーに合わせられます。 - テキスト折り返し:1文字ずつ追加しながら横幅をチェックし、はみ出したら改行するロジックです。日本語の文字境界を正確に処理できます。
- ドロップシャドウ:テキストをわずかにずらして2回描画することで影を表現しています。背景色に関わらず文字が読みやすくなる効果があります。
- Path.mkdir(parents=True):出力先ディレクトリが存在しない場合でも自動作成されるので、事前にフォルダを作る手間がありません。
まずはこのファイルを generate_eyecatch.py として保存し、python generate_eyecatch.py を実行してみてください。output/test_eyecatch.png が生成されれば成功です。
ステップ4:CSVファイルの準備
バッチ処理用のCSVを用意します。articles.csvとして保存してください。カテゴリ名が背景色の切り替えキーになります。
title,category,slug
PythonのPillowでアイキャッチ画像をゼロ円自動生成する方法,Python,pillow-eyecatch
FastAPIで作るREST API入門【5分で動く】,Python,fastapi-intro
WordPressのキャッシュ設定完全ガイド,WordPress,wp-cache-guide
Nginxでリバースプロキシを設定する手順,インフラ,nginx-reverse-proxy
VSCodeおすすめ拡張機能2024年版,ツール,vscode-extensions
Dockerで開発環境を構築する最短手順,インフラ,docker-dev-env
CSVの各列の意味は以下のとおりです。
- title:記事タイトル。そのままアイキャッチに描画されます。
- category:カテゴリ名。後述のカラーテーマ辞書と照合して背景色を自動切り替えします。
- slug:出力ファイル名に使われます。
output/pillow-eyecatch.pngのように保存されます。
ステップ5:CSVから一括バッチ生成するスクリプト
CSVを読み込み、カテゴリに応じて背景色・アクセントカラーを自動切り替えしながら複数のアイキャッチを一括生成します。出力先はoutput/ディレクトリにslug.png形式で保存されます。
import csv
from pathlib import Path
from generate_eyecatch import generate_eyecatch # ステップ3のファイル名に合わせる
# カテゴリ別カラーテーマ定義
CATEGORY_THEMES = {
"Python": {"bg": (20, 20, 40), "accent": (80, 160, 255), "text": (255, 255, 255)},
"WordPress": {"bg": (0, 50, 30), "accent": (0, 200, 100), "text": (255, 255, 255)},
"インフラ": {"bg": (40, 20, 0), "accent": (255, 160, 0), "text": (255, 255, 255)},
"ツール": {"bg": (40, 0, 40), "accent": (220, 80, 220), "text": (255, 255, 255)},
"DEFAULT": {"bg": (30, 30, 30), "accent": (255, 80, 80), "text": (255, 255, 255)},
}
FONT_PATH = "fonts/NotoSansJP-Bold.ttf"
OUTPUT_DIR = "output"
CSV_PATH = "articles.csv"
def batch_generate(csv_path: str, output_dir: str, font_path: str) -> None:
with open(csv_path, encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
title = row["title"]
category = row["category"]
slug = row["slug"]
theme = CATEGORY_THEMES.get(category, CATEGORY_THEMES["DEFAULT"])
output_path = str(Path(output_dir) / f"{slug}.png")
generate_eyecatch(
title=title,
output_path=output_path,
bg_color=theme["bg"],
accent_color=theme["accent"],
text_color=theme["text"],
font_path=font_path,
)
if __name__ == "__main__":
batch_generate(CSV_PATH, OUTPUT_DIR, FONT_PATH)
このバッチスクリプトを実行すると、CSVに記載した6記事分のアイキャッチが一気に生成されます。カテゴリが「Python」なら濃紺×青アクセント、「WordPress」なら深緑×グリーンアクセントというように、自動でテーマが切り替わります。新しいカテゴリを追加したいときは CATEGORY_THEMES 辞書にエントリを追加するだけです。
実際にこのようなアプローチを実践している事例として、【Python】面倒くさがり屋のための自動アイキャッチ作成【Pillow】 | enjoyallも参考になります。
フェーズ4:動作確認・カスタマイズと運用への組み込み
生成画像の品質確認ポイント
スクリプトが動いたら、生成された画像を必ず目視で確認しましょう。よくある問題と対処法を整理しておきます。
① 文字切れが発生していないか
タイトルが長すぎると最後の文字が画像外にはみ出すことがあります。折り返し処理は実装済みですが、1行あたりの最大文字数と font_size のバランスを確認してください。タイトルが特に長い場合は font_size を60〜64に下げると収まりやすくなります。
② 解像度は適切か
1200×630pxで生成していれば、Retina(高解像度)ディスプレイでも十分なクオリティです。ただし、WordPressのメディアライブラリにアップロードする際に自動リサイズがかかる場合があるので、テーマの推奨サイズと照らし合わせて確認しましょう。
③ ファイルサイズの最適化(WebP変換)
PNGは画質が高い反面、ファイルサイズが大きくなりがちです。ページ表示速度を改善したい場合は、WebP形式で保存するのがおすすめです。Pillowは標準でWebPに対応しています。
# PNG保存の代わりにWebPで保存する場合
img.save(output_path.replace(".png", ".webp"), "WEBP", quality=85)
quality=85 は視覚的な劣化がほぼなく、ファイルサイズをPNGの30〜50%程度に圧縮できる目安の値です。Core Web Vitals(Googleのページ体験指標)を意識するなら、積極的にWebPを採用しましょう。
④ 日本語フォントが文字化けしていないか
フォントパスが正しく指定されていない場合、テキストが豆腐(□)になります。ImageFont.truetype(font_path, font_size) の font_path を絶対パスで指定すると確実です。
import os
font_path = os.path.abspath("fonts/NotoSansJP-Bold.ttf")
応用アイデア:カテゴリ別配色の自動切り替えをさらに発展させる
フェーズ3で実装したカテゴリ別テーマは、そのまま運用に乗せられる実用的な仕組みです。さらに発展させるアイデアをいくつか紹介します。
アイデア①:サイトロゴをオーバーレイする
画像の右下や左上にサイトロゴ(PNG画像)を合成すると、ブランド感が一気に上がります。Pillowの Image.paste() メソッドを使えば数行で実装できます。
# ロゴ画像をオーバーレイする例
logo = Image.open("logo.png").convert("RGBA")
logo_w, logo_h = logo.size
logo_scale = 0.15 # キャンバス幅の15%サイズに縮小
new_w = int(width * logo_scale)
new_h = int(logo_h * (new_w / logo_w))
logo = logo.resize((new_w, new_h), Image.LANCZOS)
# 右下に配置(余白20px)
paste_x = width - new_w - 20
paste_y = height - new_h - 20
img.paste(logo, (paste_x, paste_y), logo) # 第3引数はアルファマスク
アイデア②:WordPressへの自動アップロード
生成した画像をWordPressに自動でアップロードするには、WordPress REST API(WordPressが標準で提供するHTTPベースのAPI)を使います。PythonのRequestsライブラリと組み合わせることで、生成→アップロード→記事への紐付けまでを完全自動化できます。
import requests
import base64
def upload_to_wordpress(image_path: str, wp_url: str, username: str, app_password: str) -> int:
"""生成した画像をWordPressメディアライブラリにアップロードし、メディアIDを返す"""
credentials = base64.b64encode(f"{username}:{app_password}".encode()).decode()
headers = {
"Authorization": f"Basic {credentials}",
"Content-Disposition": f"attachment; filename={image_path.split('/')[-1]}",
"Content-Type": "image/png",
}
with open(image_path, "rb") as f:
response = requests.post(f"{wp_url}/wp-json/wp/v2/media", headers=headers, data=f)
response.raise_for_status()
media_id = response.json()["id"]
print(f"[WordPress] アップロード完了 media_id={media_id}")
return media_id
WordPressの管理画面で「アプリケーションパスワード」(Application Password)を発行しておくと、セキュアに認証できます。この仕組みを組み合わせれば、記事投稿→CSV更新→アイキャッチ生成→自動ア

コメント