BloggerテーマXML自動アップロード — Playwright + 9222 CDPで手動クリック0回

1 min read · 295 words

活用法 / ブログ運営 / Python・自動化
約2,500字

Bloggerのデメリットの一つは、テーマのXMLを変更するたびに、管理画面のUIで「テーマ → バックアップ/復元 → アップロード」と3〜4回クリックし、さらにログイン確認と適用待ちまで含めると、1回につき5分近くかかってしまう点です。私たちはこれをPlaywright + Chrome 9222 CDPで自動化し、手動クリックを0回に減らしました。作成した意図や実際の動作, 効果、検証までをそのまま解説します。

開発した理由

テーマの修正作業は、頻繁に行う時期には1日に5〜10回発生します。色を1行、フォントの太さを1行、サイドバーの位置を1行変更して、本番環境(ライブ)で結果を確認するまでに毎回5分もかかっていては、作業のフローが途切れてしまいます。

また、Bloggerの古い「テーマの復元」ルートには、BTP(以前のサイト名)のような第1世代のClassic modeトリガーが存在し、一度押し間違えるとサイト全体のレイアウトが古い第1世代に逆戻りしてしまいました。私たちはこの現象を2回再現してしまった後、「復元」ルートをコード上で永久に遮断しました。安全なルートはただ一つ、管理画面の「現在のテーマのソースコードを編集」画面(CodeMirrorエディタ)にsetValueでXML全体を直接流し込む方法です。

この一連の流れを毎回手動で行わずに済むよう、自動化モジュールを作成しました。

動作原理

核心となるのは、ユーザーがすでに9222ポートで起動しているChromeにPlaywrightを接続(CDP attach)し、コマンドを送信する仕組みです。新しいブラウザを別途起動することはありません。そうすることで、Googleログインがすでに維持されているため、CAPTCHAや2段階認証(2FA)を自動的に突破できます。

手順:

  1. Chrome 9222の起動確認http://127.0.0.1:9222/json/version のレスポンスが200であれば起動中。停止していれば、ユーザーに「9222ポートでChromeを起動してください」と通知(Discord)。自動起動はOS別に分岐。
  2. Playwright CDP接続playwright.chromium.connect_over_cdp("http://127.0.0.1:9222") の1行。新規ブラウザは起動しません。
  3. ターゲットタブの探索 — すでに開いているタブの中に blogger.com/blog/themes/ があればそこへ移動。なければ新規タブを開く。
  4. 「現在のテーマのソースコードを編集」ボタンのクリック — aria-labelまたはテキストセレクター。韓国語と英語の両方のロケールに対応。
  5. CodeMirrorエディタの有効化待ちdocument.querySelector('.CodeMirror') が表示されるまでポーリング。
  6. CodeMirror.setValue(xml)の呼び出し — Playwrightのevaluate内で直接呼び出し。クリップボード経由やタイピング入力ではありません。50KBのXMLも即座に反映されます。
  7. 「テーマを保存」ボタンのクリック — 保存後、「テーマが更新されました」というトースト通知が表示されればOK。
  8. 本番環境のスポットチェック — 60秒待機後に https://blog-url/ をフェッチ。theme.xmlのセンチネルコメント(例: )がレスポンスに含まれていれば verified=True。

全工程の平均時間は30秒。手動クリックは0回。

実際の効果

  • 1回あたりのアップロード時間: 5分 → 30秒(90%短縮)
  • 累計自動アップロード回数: 導入後、約320回
  • 「復元」ルートの誤クリックによる第1世代Classicモードへの逆戻り事故: 導入前2件 → 導入後0件
  • 失敗事例: 9222 Chromeの停止4件 / Googleログインの期限切れ2件 / CAPTCHA 1件。すべてユーザーへの通知 → 手動復旧。
  • バックアップ: アップロードのたびに事前に theme.xml.before. を自動保存。アップロードに失敗した場合、1秒以内に直前のバージョンにロールバック可能。

副次的な効果として、「テーマを1行だけ変えてみよう」という気軽な実験が増えました。毎回5分かかると考えると躊躇してしまいますが、30秒であれば気軽に試せます。結果として、デザインのイテレーションが高速化しました。

検証方法

3つの検証を行いました。

XMLのラウンドトリップ検証 — アップロード後、同じページのCodeMirrorで再び getValue() を呼び出し、その値が送信したXMLとバイト単位(byte-by-byte)で同一であるかを確認します。これは、BloggerのSkinVariablesパーサーがサイレントに拒否(silent reject)するケース(CDATA内に生のHTMLトークンがある場合など)を検知するための安全網です。320回のうち、サイレント拒否を検知したケースは5件ありました。

本番環境のスポットチェック — アップロード後に60秒待機し、本番サイトをフェッチ。theme.xmlのセンチネルマーカーがレスポンスに含まれているかを確認。320回中320回すべてパス。

第1世代Classicモードへの逆戻り防止テスト — 自動化処理が意図的に「復元」ボタンを押そうとした際、コード上で遮断されるかをユニットテストで確認。assert "restore" not in click_targets で常にパス。遮断フックが正常に動作していることを確認。

実装方法

モジュール全体を移植するよりも、核心となる数行だけを取り入れるのが実用的です。

まず、Chromeを9222ポートで起動します。


# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" \
 --remote-debugging-port=9222 \
 --user-data-dir=C:\chrome_debug_profile

# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
 --remote-debugging-port=9222 \
 --user-data-dir=$HOME/chrome_debug_profile

次に、Playwrightで接続(attach)します。


import asyncio
from playwright.async_api import async_playwright

BLOG_ID = "1234567890"
THEME_XML = open("theme.xml", encoding="utf-8").read()

async def upload_theme(xml: str):
 async with async_playwright() as p:
 browser = await p.chromium.connect_over_cdp("http://127.0.0.1:9222")
 ctx = browser.contexts[0]
 page = await ctx.new_page()
 await page.goto(f"https://www.blogger.com/blog/themes/{BLOG_ID}")
 # 「現在のテーマのソースコードを編集」
 await page.click("text=현재 테마의 소스 코드 수정")
 await page.wait_for_selector(".CodeMirror", timeout=30000)
 await page.evaluate(
 "(xml) => document.querySelector('.CodeMirror').CodeMirror.setValue(xml)",
 xml,
 )
 await page.click("button:has-text('테마 저장')")
 await page.wait_for_selector("text=테마가 업데이트되었습니다", timeout=60000)
 await browser.close()
 print("uploaded")

asyncio.run(upload_theme(THEME_XML))

ここで重要なのは、connect_over_cdpの1行とCodeMirror.setValueの1行です。残りはセレクターを調整する作業です。

復元ルートは絶対にクリックしないでください。同じページに「復元」ボタンが並んでいますが、それは私たちの事故事例の原因です。一度クリックすると第1世代のClassicモードに逆戻りし、本番サイトが崩れてしまいます。自動化処理においては、そのセレクターをコード上で明示的に遮断しておくのが安全です。

要約: Chromeを9222ポートで起動し、Playwrightで接続(attach)した後にCodeMirror.setValueを1行呼び出すだけで、Bloggerテーマの自動アップロードが可能になります。最初のセットアップさえ済ませておけば、その後はコマンド1行ですべてのアップデートが

Category Coverage Notice

This article follows our label-specific editorial criteria. Details:

ToolSignal Pro Editorial

ToolSignal Pro는 AI·IT·소프트웨어 트렌드를 다루는 종합 IT 인사이트 매거진입니다.

이전 글 다음 글