アイキャッチ画像を毎記事Canvaで手作りして、気づけば30分消えていませんか?PythonのPillowなら、タイトルを渡すだけで統一感のあるアイキャッチをゼロ円・数秒で量産できます。この記事では、インストールから実際に動くスクリプトの完成まで、ハナが丁寧に解説します。
フェーズ1:なぜアイキャッチ作成が「地味にしんどい」のか
ブログを運営していると、記事を書き終わった後に「アイキャッチどうしよう」という壁が必ずやってきます。CanvaやAdobe Expressを開いて、テンプレートを選んで、タイトルを打ち込んで、フォントを調整して、書き出して……。慣れていても1枚あたり15〜30分はかかるのが正直なところです。
月10記事を書くブロガーを例に試算してみましょう。1枚あたり20分かかるとすると、月200分=3時間20分以上がアイキャッチ作業だけで消えます。記事の品質を上げる時間、SEO(検索エンジン最適化)を研究する時間、新しいジャンルをリサーチする時間——そういった本質的な投資に使えるはずの時間が、ひたすら画像を手作りする作業に吸われていくのです。
「それなら画像生成AIを使えばいいのでは?」という声もよく聞きます。確かにMidjourneyやAdobe Fireflyなどのツールは品質が高く魅力的です。ただし、個人ブロガーにとってはいくつかのハードルがあります。
- 月額課金が重い:本格的なプランは月2,000〜5,000円以上になることも珍しくありません。記事の収益化が安定するまでは、このコストが地味に痛いです。
- 本数制限がある:無料プランや安価なプランでは月間の生成枚数に制限があり、量産ブログには対応しきれないケースがあります。
- 著作権・利用規約リスク:商用利用の可否や、生成画像の権利帰属がサービスによって異なります。ブログの収益化を前提にすると、利用規約を慎重に確認する必要があります。
- ブランドの統一感が出しにくい:AIが生成するビジュアルはクオリティが高い一方、毎回テイストが変わりやすく、ブログ全体の統一感を保つのが難しいです。
ここでPythonのPillow(ピロー)が登場します。Pillowは画像処理ライブラリで、完全無料・生成枚数無制限・一度作れば全記事に自動適用という三拍子が揃っています。デザインのクオリティはAI生成には劣りますが、「タイトルが読みやすく、ブランドカラーで統一されたシンプルなアイキャッチ」という目的においては十分すぎる性能を発揮します。実際、面倒くさがり屋のための自動アイキャッチ作成を実践した事例でも、Pillowが選ばれる理由はこのシンプルさと運用コストのゼロにあります。
ハナが特に推したいのは「一度スクリプトを書けば、あとはタイトルを渡すだけ」という点です。ツールを毎回開く必要もなく、ログインも不要。完全にローカルで完結するので、インターネット環境がなくても動きます。これは量産ブログを運営する方にとって、じわじわと効いてくる大きなメリットです。
フェーズ2:Pillowで何ができるか・設計方針を決める
実装に入る前に、Pillowの基本機能と今回作るアイキャッチの設計方針を整理しておきましょう。「何ができるか」を把握しておくと、後のカスタマイズがぐっとやりやすくなります。
Pillowの主要機能
Pillowは、Python Imaging Library(PIL)のフォーク(派生プロジェクト)として活発にメンテナンスされているライブラリです。今回使う主な機能は以下の通りです。
- Image.new():指定した色・サイズの新規画像を生成します。背景の土台を作る最初のステップです。
- ImageDraw(イメージドロー):画像上に矩形・線・テキストなどを描画するモジュールです。背景のアクセントラインやテキスト描画に使います。
- ImageFont(イメージフォント):TTFファイルを読み込んでフォントを指定するモジュールです。日本語フォントを使う際に必須です。
- Image.paste():別の画像を合成します。ロゴや透かし画像をアルファチャンネル(透明度情報)付きで重ねる際に使います。
- img.save():生成した画像をJPEGやPNGとして保存します。
詳しい使い方はPillowを使った画像処理のチートシートも参考になります。基本的な操作がひと目でわかるまとめ方をされているので、ブックマークしておくと便利です。
アイキャッチの設計方針
今回作るアイキャッチは、OGP(Open Graph Protocol:SNSシェア時に表示されるサムネイル)にも対応できるよう1200×630pxを基準サイズにします。このサイズはFacebookやTwitter(現X)のOGP推奨サイズに準拠しており、WordPressのアイキャッチ画像としても最適です。
構成要素はシンプルに絞ります。
- 背景色:カテゴリ別に定義したダークカラーを使用。目に優しく、テキストが映えます。
- アクセントライン:画像の上下にブランドカラーの細いラインを引いてメリハリを出します。
- タイトルテキスト:自動折り返し+センタリングで、どんな長さのタイトルでも崩れないようにします。
- サイトラベル:右下にサイト名を小さく表示してブランディングを強化します。
日本語フォントの選定と配置
Pillowで日本語テキストを描画するには、日本語対応のTTFフォントが必要です。ハナのおすすめはNoto Sans JP(ノトサンズジェイピー)です。Google Fontsで公開されており、OFL(オープンフォントライセンス)で商用利用も無料。ウェイトバリエーションも豊富で、ブログのアイキャッチには「Bold」が最適です。
Google Fontsのサイトから「Noto Sans JP」を検索し、「Download family」からダウンロードしてください。解凍後、NotoSansJP-Bold.ttfをプロジェクトのfonts/フォルダに配置します。システムフォントを直接参照する方法もありますが、プロジェクト内に置く方が環境依存を避けられるため、チームや複数PCで使う際にも安心です。
なお、Pillowでの画像生成・保存の基本的な流れについてはPythonライブラリ Pillow で画像生成&保存する – dotTrailの解説も非常に丁寧にまとまっています。初めてPillowを触る方はあわせて読んでみてください。
フェーズ3:実装手順——インストールから自動生成スクリプト完成まで
ここからが本番です。実際に手を動かしながら読み進めてください。ハナが一つひとつのステップで「なぜこう書くのか」も補足しながら解説します。
Step 1:必要なライブラリのインストールと環境確認
PillowはPIPで導入できます。NotoSansJPフォントはGoogle Fontsからダウンロードし、プロジェクトディレクトリのfonts/フォルダに配置してください。
pip install Pillow
ディレクトリ構成は以下を想定しています。
project/
├── fonts/
│ └── NotoSansJP-Bold.ttf
├── output/
├── titles.csv
├── generate_ogp.py
└── batch_generate.py
インストール後、python -c "from PIL import Image; print(Image.__version__)" を実行してバージョンが表示されれば準備完了です。output/フォルダはスクリプト内で自動生成されますが、あらかじめ作っておくと確認がしやすいです。
Step 2:アイキャッチ画像を生成するコア関数
背景色・サイズ・タイトルテキストを引数で受け取り、日本語テキストを自動折り返し&センタリングして1200×630pxのJPEGを保存する関数です。テキストの折り返し幅はフォントサイズと画像幅から動的に計算します。
from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
import textwrap
def generate_ogp_image(
title: str,
output_path: str,
bg_color: tuple = (30, 30, 60),
text_color: tuple = (255, 255, 255),
accent_color: tuple = (99, 179, 237),
font_path: str = "fonts/NotoSansJP-Bold.ttf",
width: int = 1200,
height: int = 630,
font_size: int = 64,
) -> None:
img = Image.new("RGB", (width, height), color=bg_color)
draw = ImageDraw.Draw(img)
# アクセントライン(上下)
line_h = 12
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)
small_font = ImageFont.truetype(font_path, 28)
# 折り返し幅を文字数で計算(全角基準)
max_chars = int((width - 160) / (font_size * 0.95))
lines = textwrap.wrap(title, width=max_chars)
# テキスト全体の高さを計算してセンタリング
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)
# サイトラベル(右下)
label = "Tech Blog"
label_bbox = draw.textbbox((0, 0), label, font=small_font)
label_w = label_bbox[2] - label_bbox[0]
draw.text((width - label_w - 40, height - 60), label, font=small_font, fill=accent_color)
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
img.save(output_path, "JPEG", quality=92)
print(f"生成完了: {output_path}")
if __name__ == "__main__":
generate_ogp_image(
title="PillowでブログのOGP画像をゼロ円で自動生成する方法",
output_path="output/sample.jpg",
bg_color=(20, 30, 48),
accent_color=(99, 179, 237),
)
このコードのポイントを補足します。textwrap.wrap()で日本語タイトルを自動折り返ししているため、タイトルが長くても短くても画像が崩れません。draw.textbbox()でテキストの実際の描画幅を取得してからX座標を計算することで、正確なセンタリングが実現できます。影(シャドウ)を3px右下にずらして先に描画することで、背景色との対比が弱い場合でもテキストの視認性を確保しています。
Step 3:カテゴリ別カラーパレットの定義
CSVのカテゴリ列に対応するカラーパレットを辞書で管理します。カテゴリが未定義の場合はデフォルト値にフォールバックします。
CATEGORY_PALETTES = {
"python": {
"bg_color": (20, 30, 48),
"text_color": (255, 255, 255),
"accent_color": (99, 179, 237),
},
"javascript": {
"bg_color": (40, 38, 10),
"text_color": (255, 255, 255),
"accent_color": (247, 220, 60),
},
"devops": {
"bg_color": (10, 40, 20),
"text_color": (255, 255, 255),
"accent_color": (72, 199, 142),
},
"design": {
"bg_color": (50, 10, 40),
"text_color": (255, 255, 255),
"accent_color": (237, 100, 166),
},
"default": {
"bg_color": (30, 30, 30),
"text_color": (255, 255, 255),
"accent_color": (160, 160, 160),
},
}
カテゴリ別にパレットを辞書で持つ設計のメリットは、新しいカテゴリを追加するときにこの辞書に1エントリ追加するだけで済む点です。スクリプト本体を修正する必要がないため、ブログのカテゴリが増えても保守しやすい構造になっています。
Step 4:CSVからの一括バッチ生成スクリプト
CSVファイルには title・slug・category の3列を用意します。スラッグをファイル名に使うことで出力ファイルの管理が楽になります。既存ファイルはスキップする設計にしているため、差分生成にも対応しています。
import csv
from pathlib import Path
from generate_ogp import generate_ogp_image, CATEGORY_PALETTES
def batch_generate(
csv_path: str = "titles.csv",
output_dir: str = "output",
font_path: str = "fonts/NotoSansJP-Bold.ttf",
skip_existing: bool = True,
) -> None:
results = {"success": 0, "skipped": 0, "error": 0}
with open(csv_path, encoding="utf-8", newlin
上記はバッチスクリプトの冒頭部分です。titles.csvの形式は以下のように用意してください。
title,slug,category
PillowでブログのOGP画像をゼロ円で自動生成する方法,pillow-ogp-auto,python
JavaScriptでAPIを叩いてデータを取得する方法,js-api-fetch,javascript
DockerでPython環境を構築する手順,docker-python-setup,devops
バッチスクリプトの中では、各行のcategory値でCATEGORY_PALETTESを参照し、対応するカラーパレットを取得します。未定義のカテゴリはCATEGORY_PALETTES.get(category, CATEGORY_PALETTES["default"])でデフォルトパレットにフォールバックします。出力ファイルが既に存在する場合はskip_existing=Trueでスキップするため、新記事分だけを追加生成する差分運用が可能です。
スクリプトが完成したら、以下のコマンドで一括生成を実行できます。
python batch_generate.py
CSVに100行あれば100枚のアイキャッチが数十秒で生成されます。Canvaで手作りしていた時間と比べると、その差は歴然です。
フェーズ4:動作確認・カスタマイズ・WordPress連携への応用
スクリプトが動いたら、次は品質確認とさらなる活用を考えていきましょう。せっかく自動化するなら、WordPress連携まで視野に入れておくと運用がぐっとラクになります。
生成画像のチェックポイント
生成した画像をoutput/フォルダで開いて、以下の点を目視確認してください。
- 文字切れがないか:タイトルが長い場合、折り返し計算が正しく機能しているか確認します。
max_charsの計算式は全角文字基準ですが、英数字混在タイトルでは若干ずれることがあります。その場合はfont_size * 0.95の係数を0.85〜0.90に調整してみてください。 - 余白のバランス:テキストが画像の端に近すぎないか確認します。
width - 160の160という値が左右の余白(各80px相当)になっています。窮屈に感じる場合は200〜240に増やすと落ち着きます。 - 解像度と容量:JPEG quality=92で保存しているため、ファイルサイズは通常100〜200KB程度になります。WordPressの推奨アップロードサイズ内に収まっているか確認してください。容量を削減したい場合はquality=85程度まで下げても視覚的な劣化はほぼわかりません。
- フォントの読みやすさ:ダークな背景にホワイトテキストの組み合わせは視認性が高いですが、モバイル表示でも確認しておくと安心です。
カテゴリ別カラーパレットでブランド感を高める
フェーズ3で定義したCATEGORY_PALETTESを活用すると、カテゴリごとに異なるビジュアルテーマを持たせることができます。たとえば「Python記事はネイビー×ブルー」「JavaScript記事はダークイエロー×ゴールド」というようにカテゴリとカラーを紐づけることで、読者がサムネイルを見ただけでカテゴリを識別できるようになります。
これはブログの回遊率(読者が複数記事を読む割合)にも好影響を与えます。「あ、このブルーのサムネはPython記事だ」という視覚的な記憶が読者に生まれると、関連記事への誘導がスムーズになるからです。
バッチ処理の拡張:MarkdownフロントマターからのCSV生成
Hugoやjekyllなどの静的サイトジェネレーター(テキストファイルからウェブサイトを生成するツール)を使っている場合、Markdownファイルのフロントマター(記事の冒頭にあるメタデータ部分)からタイトルとスラッグを自動抽出してCSVを生成するスクリプトを組み合わせると、さらに手作業が減ります。
import frontmatter # pip install python-frontmatter
import csv
from pathlib import Path
posts_dir = Path("content/posts")
rows = []
for md_file in posts_dir.glob("*.md"):
post = frontmatter.load(md_file)
rows.append({
"title": post.get("title", ""),
"slug": md_file.stem,
"category": post.get("categories", ["default"])[0].lower(),
})
with open("titles.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["title", "slug", "category"])
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)}件のタイトルをCSVに出力しました")
このスクリプトとbatch_generate.pyを組み合わせれば、「Markdownを書く→CSVを自動生成→アイキャッチを一括生成」という完全自動パイプラインが完成します。
WordPress REST APIとの連携
生成したアイキャッチ画像をWordPressに自動アップロードするには、WordPress REST API(WordPressが提供するプログラム間連携の仕組み)を使います。大まかな流れは以下の通りです。
- WordPressのアプリケーションパスワード(管理画面→ユーザー→プロフィールから生成)を取得します。
- Pythonの
requestsライブラリで/wp-json/wp/v2/mediaエンドポイントに画像をPOSTします。 - 返却されたメディアIDを使って、
/wp-json/wp/v2/posts/{id}エンドポイントで記事のアイキャッチ(featured_media)を更新します。
この連携まで組み込むと、CSVに記事情報を追加するだけでアイキャッチ生成からWordPressへのアップロードまでが完全自動化されます。記事の投稿自体もREST API経由で自動化すれば、コンテンツ運用の大部分をPythonスクリプトに任せることができます。
ハナの所見
今回のPillow活用、ハナが実際に試してみて感じたのは「完璧を目指さないことの大切さ」です。AI生成画像のような写実的なビジュアルは作れませんが、ブログのアイキャッチとして必要なのは「タイトルが読めること」「統一感があること」「すぐに用意できること」の3つです。Pillowはこの3つを完璧に満たしています。
特に量産型のブログ(週3〜5記事を継続するスタイル)においては、アイキャッチ作成のボトルネックが記事更新の頻度を下げる原因になりがちです。「画像を用意するのが面倒だから、今日は1記事にしておこう」という判断が積み重なると、長期的な記事数の差は大きくなります。自動化でそのブレーキを外すことが、ブログ成長への近道になると感じています。
また、一度スクリプトを書いてしまえば、フォントやカラーパレットを変えるだけでブログのリニューアルにも対応できます。Canvaのテンプレートを全記事分作り直す手間と比べると、コードを数行変えて一括再生成できるPillowの柔軟性は圧倒的です。
Pillowの活用をさらに深めたい方、そして「自動化の発想を体系的に学びたい」という方には、以下の書籍・講座もおすすめです。
📚 Pythonによるスクレイピング&機械学習 開発テクニック
Pythonで「面倒な作業を自動化する」発想と実装パターンが体系的に学べるため、Pillow活用の次のステップにも直結する。

コメント