2 min read · 362 words
活用テクニック / ブログ運営 / Python・画像自動化
約2,500字
How-to記事やチュートリアルは、文字よりも1枚のスクリーンショットの方が強力です。しかし、私たちのサイトの記事を見ると、競合他社のほとんどは注釈のないスクリーンショットをただ貼り付けているだけです。赤枠でクリック位置を指し示すことも、矢印で視線の流れを誘導することもありません。私たちは、ガイド記事に必ず注釈入りのスクリーンショットが挿入されるよう自動化しました。どのように作り、どのように動作し、どのような効果があり、どのように検証したのかを解説します。
開発した理由
How-to記事で最もよくある失敗は、「このボタンを押してください」というテキストの横に、どのボタンを指しているのか分からないスクリーンショットが貼られているケースです。読者は画面を左右にスキャンしながら1〜2秒を無駄にし、その1〜2秒が離脱につながります。
また、ガイド記事におけるSEOの核心的な差別化要因は、「この記事が本当にステップバイステップでサポートしてくれる」というシグナルです。注釈のないスクリーンショットでは、そのシグナルが弱くなります。赤枠+番号+矢印が入ったスクリーンショットは、一目で「この記事は本物のガイドだ」という印象を与えます。
問題は、人がすべての記事に注釈を入れると1〜2時間ずつかかることです。Photoshop、Figma、Snagitなどのツールで、枠線 → 矢印 → 番号 → テキスト → 保存。記事が10本あれば10〜20時間かかります。だから自動化しました。
動作原理
全体の流れは4ステップです。
1. スクリーンショットの収集
H2セクション内のキーワード(例:「Google Search Console 設定」)を抽出し、Bing Image Search APIで候補を5つ取得します。Bingを使用する理由は、SafeSearchやライセンスフィルターが強力であり、無料のAPIキー提供枠が十分に大きいためです。
import httpx
import os
BING_API_KEY = os.environ["BING_API_KEY"]
def search_screenshot(keyword: str, count: int = 5) -> list[dict]:
r = httpx.get(
"https://api.bing.microsoft.com/v7.0/images/search",
headers={"Ocp-Apim-Subscription-Key": BING_API_KEY},
params={"q": f"{keyword} screenshot", "count": count,
"license": "ShareCommercially", "imageType": "Photo"},
timeout=15,
)
return [{"url": v["contentUrl"], "thumb": v["thumbnailUrl"],
"w": v["width"], "h": v["height"]}
for v in r.json().get("value", [])]
2. 候補の自動フィルタリング
取得した5つのうち、小さすぎるもの(幅600px未満)、大きすぎるもの(5MB超)、ウォーターマーク(透かし)が疑われるもの(ベンダーロゴの複数検出)を排除します。残った1〜2枚だけを次のステップに進めます。
3. PILで注釈を入れる
最も重要なステップです。Pillowを使って、枠線、矢印、番号、テキストを描画します。
from PIL import Image, ImageDraw, ImageFont
def annotate(img_path: str, boxes: list[dict], out_path: str) -> None:
"""boxesの例: [{'rect': (x,y,w,h), 'label': '1', 'text': 'ここをクリック'}]"""
im = Image.open(img_path).convert("RGB")
draw = ImageDraw.Draw(im)
font_big = ImageFont.truetype("fonts/pretendard/Pretendard-Bold.ttf", 28)
font_small = ImageFont.truetype("fonts/pretendard/Pretendard-Medium.ttf", 18)
for b in boxes:
x, y, w, h = b["rect"]
# 赤い枠線 (外枠3px)
draw.rectangle([x, y, x + w, y + h], outline="#dc2626", width=3)
# 左上に番号バッジ
draw.ellipse([x - 14, y - 14, x + 28, y + 28], fill="#dc2626")
draw.text((x + 7, y - 8), b["label"], fill="#fff", font=font_big)
# 枠線の下に説明を1行追加
if b.get("text"):
draw.text((x, y + h + 8), b["text"], fill="#dc2626", font=font_small)
im.save(out_path, "JPEG", quality=88, optimize=True)
枠線の座標は、LLMに「スクリーンショットのどこを強調すればよいか」を問い、返ってきた比率座標(0〜1の範囲)をピクセルに換算します。
4. ImgBBへのアップロード + 本文への挿入
作成した注釈付きJPGをImgBB APIでアップロードし、恒久的なURLを取得します。本文の該当するH2セクションの末尾に を挿入します。
publish_post の hook chain のステップの一つとして呼び出します。ガイド記事(post_type=howto)の場合のみ実行され、比較記事やニュース記事ではスキップ(skip)します。
実際の効果
- ガイド記事の平均滞在時間: 1分30秒 → 2分25秒 (+60%)
- ガイド記事の検索結果1ページ目表示率 (GSC): 18% → 34%
- 手作業の削減時間: 記事1本あたり1〜2時間 → 0秒
- 累計自動注釈スクリーンショット公開数: 約240枚 (サイト全体)
- Photoshop / Figma の使用回数: 導入後0回
最大の効果は、記事1本の公開にかかる時間の短縮です。記事本文の作成 + 自動注釈スクリーンショット → 公開まで平均20分。以前は同じ記事に3〜4時間かかっていました。
検証方法
3つの方法で検証しました。
A/Bテストによる滞在時間 (sess 88 ローンチ後6週間)
自動注釈モジュール適用前のガイド記事8本の平均滞在時間 vs 適用後の8本の平均を比較。1分30秒 → 2分25秒。p < 0.01 で統計的に有意。
ビジュアル回帰テスト
同じ入力(スクリーンショット + 枠線座標のJSON)で2回PIL処理を行った際、出力されるJPGがバイト単位(byte-for-byte)で同一であることを確認。40/40でべき等(idempotent)を確認。
CTRの比較 (検索結果のサムネイル)
GSCの検索結果ページにおいて、私たちの記事のサムネイル(Open Graph画像)が赤枠付きのスクリーンショットである場合のCTRを測定。注釈なしのサムネイル8本の平均CTRは2.1%、注釈ありのサムネイル8本の平均CTRは4.3%。2倍の差。
実装方法
核となる関数を1つ導入するだけでも効果があります。上記の annotate コードが核心です。枠線の座標をどのように取得するかが次の課題ですが、2つの方法があります。
方法 1: LLMに聞く
ClaudeやGeminiなどのVisionモデルに、スクリーンショット + 「この画面でユーザーがクリックすべき位置3箇所を比率座標で教えて(例:x=0.4, y=0.6, w=0.12, h=0.05)」と指示します。JSON形式で応答を受け取り、ピクセルに換算します。
方法 2: OpenCVのテンプレートマッチング
特定のUI要素(ボタン画像など)のテンプレートを事前に保存しておき、 cv2.matchTemplate で位置を自動検出します。正確ですが、UIが頻繁に変更される場合、テンプレートのメンテナンス負荷が高くなります。
私たちは方法1(LLM)を採用しています。UIの変化に強く、記事のテーマごとに異なるUIにも自動で適応できます。トークン費用と応答時間(平均3秒)さえ許容できれば強力です。
まとめ:ガイド記事に注釈のないスクリーンショットをそのまま載せるのはやめましょう。PILで枠線を1本描くコード5行 + LLMによる座標取得1回 = 記事のクオリティが一段階アップします。記事1本あたり1〜2時間の節約はおまけのようなものです。
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: