Help us understand the problem. What is going on with this article?

定点観測したいサイトのデザイン変更をCSSのdiffで検知する

More than 1 year has passed since last update.

この記事はLIFULL Advent Calendar その3の遅れてきた1日目の記事です。

他のWEB企業でもそうだと思うのですが、SEO等のサイトの改善施策は「これをやれば当たる!」というのが最初から明確に分かるわけではありません。その際に、施策の弾数を増やすために参考となるベースラインとなるサイトをいくつか手動でチェックしていました。

ただ、WEBサイトのチェックには膨大な手間がかかってしまうため、時間や網羅性で問題がありました。そのため、簡単にできて対象サイトに負荷をかけない範囲で、ベースラインにしているサイトをクローリングして、サイトの変更を検知する処理をRubyで実装し、運用しています。

基本方針

実装方針は簡単で、基本的に以下の2つの処理を行っているにすぎません。

  • CSSを取得してAWSのS3に保存する
  • 前日に取得したCSSと比較して、差分があれば通知

CSSの差分を検知することで、テキストファイルの単純比較で変更数が検知できます。他の実装案にはこのようなものがあったのですが、実装や運用が難しかったため諦めました。

そのため、以下のような欠点(制限)があります。

  • CSSに影響を与えない差分は検知できない
  • A/Bテストを取り逃す可能性がある もしくはA/Bテストの期間中検知され続ける
    • ただし、ベースラインとなるサイトではこの問題は起きていないので対応していません
  • JavaScriptが多用されたサイト(PWAなど)には向かない
    • ただ、これはHeadless Chromeなどで対応できると思います

①HTMLから不変の要素を取得する

HTMLから、idやclassのみを抽出して比較しようとしていました。ただ、特に商品一覧ページのような「同じ要素が複数あり、中身のタグ要素が微妙に違う場合のあるもの」を比較する際に、非常にややこしくなってしまったので諦めました。

また、ECサイトでは商品ページが数が多くSEOの観点では重要らしいのですが、商品ごとに表示項目が微妙に違って単純比較できない場合がありました。

②画面キャプチャを比較する

画像の差分を撮るのが非常に難しいので諦めました。

ハマりどころや工夫

発想自体は簡単なのですが、いくつか実装していく中で工夫した点を説明します。

商品が消える場合がある

前述のように商品ページはけっこう重要で定点観測したいものらしいのですが、たまに商品自体が消えてしまうことがあるため、ずっと同じページを観測することができません。

そこで、以下のような設定ファイルで記述できるようにし、商品一覧(list)のページをクロールする際に、「ページ内に存在するURLの中から最初に正規表現にマッチしたものを利用する」ように実装しました。

:devices:
  pc: '****-bot'
  sp: 'iPhoneのユーザーエージェント'
:robots:
  pc: https://www.homes.co.jp/robots.txt
  sp: https://www.homes.co.jp/robots.txt
:nodes:
  - name: top
    type: top
    url:
      pc: https://www.homes.co.jp/
      sp: https://www.homes.co.jp/smp/
  - name: chintai-top
    type: top
    url:
      pc: https://www.homes.co.jp/chintai/
      sp: https://www.homes.co.jp/smp/chintai/
  - name: chintai-list
    type: list
    url:
      pc: https://www.homes.co.jp/chintai/tokyo/list/
      sp: https://www.homes.co.jp/smp/chintai/tokyo/list/
    childs:
      - name: chintai-detail
        type: detail
        url_regexp: !ruby/regexp /^https?:\/\/www.homes.co.jp\/chintai\/b-\d+\//

前述の通り、「yamlを簡単に記述できる実行ファイルとして利用する」という発想は、ワークフローエンジンのDigDagから得ました。

ちなみに、クローラーの実行前にテストコードを動かし、robots.txtを参照してクローリングが禁止されていないことをきちんとチェックしています。その際にrobotexというgemが便利でした。

baseタグのhref指定

CSSのパスに相対パスが指定されていた場合、そのページ自身のURIを基準としてページを取得します。ただし、baseタグの中で、相対パスの基準となるURIを指定することができるため、その対応を忘れないようにする必要があります。

diffのわかりやすい表示が難しい

当初、Rubyでうまく差分の通知を行おうと思っていたのですが、案外人間に読みやすい形で表現するのが難しかったです。結局シェルスクリプトのdiffコマンドに適当なオプションをつけて実行するようにしました。

キャッシュバスターの処理

CSS等の末尾に日付などの文字列が付加されている場合があり、これらをファイル名やファイルの内容からうまく除去した上でないと比較できません。これを除去するためにけっこう愚直で泥臭い実装をしています。

これはキャッシュを除去するために付加されているらしく、キャッシュバスターと呼ばれるそうです。

今後の展望

CSSの差分が検知され、変更箇所のヒントはあるものの、基本的にレポーティングは手動です。その運用やアナウンスもけっこう手間なので、少しずつ自動化していきたいと思っています。

当初、こういう要望があると聞いて業務の合間にサクッと作ってしまったのですが、意外に社内でも参考にしてもらえているようです。その際に他の人でもメンテナンスしやすいよう、社内で普及しているRubyを採用しました(が、システム面は結局自分がメンテナンスしてしまっています)。

例えば、レポーティングが簡単に行えるよう、画面キャプチャを撮って、前日分と左右に並べて保存することはそれなりに簡単にはできると思います。

ninomiyt
株式会社LIFULLの愉快なPythonistaです
https://medium.com/@ninomiyt
lifull
日本最大級の不動産・住宅情報サイト「LIFULL HOME'S」を始め、人々の生活に寄り添う様々な情報サービス事業を展開しています。
https://lifull.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away