0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WordPressメンテ前後のスクショ比較を実装する

0
Posted at

WordPress の保守作業で「更新前後のサイトのスクリーンショットを比べて異常検知する」というアプローチがあります。レイアウト崩れ・404 になった画像・テーマの崩壊などを目視に頼らず自動で拾えれば、運用がだいぶ楽になる。

ビフォアを撮る → 更新する → アフターを撮る → 差分が大きければ異常、と書くとシンプル。
実際に自動化ツール【Playwright】で組んでみたら、想像していたより問題が次々と出てきました。実装中に踏んだ3つを残しておきます。


問題① wait_until='networkidle' は地雷

ページの読み込み完了を待つ設定として、まず思いつくのが Playwright の wait_until='networkidle'

page.goto(url, wait_until='networkidle')

ネットワーク通信が一定時間途絶えるまで待つ、いかにも丁寧な待機方法。

…これが特定の WordPress サイトで毎回タイムアウトする問題が出てきました。

原因は Google Analytics と Google Tag Manager。これらはページが開いている間ずっと裏で通信し続けているので、networkidle に到達する瞬間が来ません。30秒のタイムアウトで強制終了されます。

しかも、タイムアウトすると Playwright は HTTP ステータスを「0」として返してきました。その値を「異常 → ロールバック」と判断する処理を組んでいたので、サイトは普通に表示されているのにロールバックが走るという二次災害まで発生。

WordPress のように GA/GTM が入っているのが当たり前のサイトでは、networkidle は使い物にならない、と早めに割り切る方が安全です。


問題② wait_until='load' でも撮影タイミングがブレる

networkidle をやめて wait_until='load' に切り替え。HTML・CSS・画像の読み込みが終わった時点を指す、より素直な待機条件です。

page.goto(url, wait_until='load')
page.screenshot(path=...)

これで毎回30秒タイムアウトする問題は解消。

ところが今度は、ビフォアとアフターで撮影タイミングがブレる問題が出てきました。同じサイト、同じ手順なのに、毎回少しずつ差分が検知される。

調べてみると、load が来るタイミングは毎回微妙にズレていました。

  • スライダー初期化が load 前に終わっているケース
  • スライダー初期化が load 後にずれ込むケース
  • Web フォントの描画が間に合っているケース/いないケース
  • 遅延読み込み画像が間に合っているケース/いないケース

JS の進み具合は毎回違うload で揃えたつもりでも、実際の描画状態は揃わない。

解決策は、load 完了後にさらに 1秒待つ

page.goto(url, wait_until='load')
page.wait_for_timeout(1000)
page.screenshot(path=...)

ポイントは「タイミングを揃える」ではなく「両方とも落ち着いた状態になるまで待つ」と発想を変えること。JS の初期化はだいたい load 後 200〜800ms 以内に終わるので、1秒待てば、load が早かろうが遅かろうが、両方とも初期化完了後の状態になります。絶対時刻は揃わなくても、状態が揃えば比較は成立する。

地味な1行ですが、誤検知が劇的に減りました。


問題③ ランダム要素はピクセル比較を壊す

タイミング問題が片付いたあとに残る、もう一段大きな壁。それがランダム要素でした。

webサイトには、ロードするたびに見た目が変わる領域がそこらじゅうにあります。

  • スライダー / カルーセルの開始スライドがランダム
  • 「関連記事」「人気記事」「おすすめ記事」のシャッフル表示
  • お客様の声のローテーション
  • AdSense・アフィリエイト広告の毎回違うバナー
  • 「X分前」「X時間前」などの相対時刻、現在時刻ウィジェット
  • アニメーション GIF・自動再生動画(撮影タイミングでフレームが違う)
  • フェードイン・パララックスアニメ(進捗が毎回違う)
  • CAPTCHA・トークン埋め込み

サイト側の挙動が決定的でないので、ピクセル単位の差分検知では「どれが本物の異常で、どれがランダムなだけか」を区別できません。閾値を緩めれば誤検知は減りますが、本来検知したいレイアウト崩れまで見逃しやすくなる。

対処パターン

実装側でやれることは、おおむね4種類。

1. Playwright の mask パラメータで該当領域をマスクする

slider = page.locator('.slick-slider')
ads = page.locator('.ad-banner')
page.screenshot(path=..., mask=[slider, ads])

撮影時にマスクしておけば、ランダム要素は単色で塗りつぶされ、比較から除外されます。一番素直な対処。

2. CSS 注入で該当要素を非表示にする

page.add_style_tag(content='.slider, .ad-banner, .related-posts { visibility: hidden !important; }')
page.wait_for_timeout(100)
page.screenshot(path=...)

要素自体を消すと隣接レイアウトも詰まることがあるので、display: none ではなく visibility: hidden(場所だけ確保)が無難。

3. JS でランダム要素を固定状態にする

たとえばスライダーなら、初期スライドを必ず1枚目に固定するスクリプトを撮影前に注入する:

page.evaluate("document.querySelectorAll('.slider').forEach(s => s.scrollLeft = 0)")

要素のセレクタや API に依存するので、サイトごとにメンテが必要。

4. 差分閾値を緩める

ピクセル差分の許容率を 8% → 15% などに引き上げる。実装は楽ですが、本物のレイアウト崩れまで通してしまうので、最後の手段。


ランダム要素は WordPress テーマやプラグインの仕様であって、こちら側で「無くす」ことはできません。スクショ比較というアプローチには本質的な限界があり、対象サイトの構造に応じて補正が必要、と理解した上で導入するのが現実的です。

まとめ

スクショ比較を実装するときに踏みやすい3つのポイント:

  • wait_until='networkidle' は GA/GTM のあるサイトで永遠にアイドルにならない。'load' を使う
  • 'load' 後にさらに1秒待つ。「タイミングを揃える」ではなく「状態を揃える」
  • ランダム要素はマスク・非表示・固定化のどれかで除外する。閾値で逃げるのは最後の手段

「ビフォアとアフターを比べるだけ」と思っていた処理が、実装してみると思った以上に複合的でした。同じことをしようとしている方の参考になれば嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?