Django でも Guess.js は使えますが、クライアント側での仕込みになり SSR を行うページで使用することは基本的にできません。
このため、動的に<link rel="prefetch">追加する kemsakurai/django-guess: django-guessいうライブラリを作成してみました。
以下説明を記載します。


作った動機

作成の動機は以下になります。

  1. 通常のページでは、Guess.js を使って Google Analytics のアクセスの結果に基づいた ページの先読みが可能。
  2. AMP ページでも ServiceWorker からの先読みはできそう。
  3. ServiceWorker を使わなくても、<link rel="prefetch"> AMP のページごとに追加できれば良い。
  4. Guess.js のやりたいことは、Python でもできそう。
  5. Django の Command と Template タグを作る。

getting started

  • インストール

    pip install git+https://github.com/kemsakurai/django-guess      
    

  • settings.py の INSTALLED_APPS の編集
    INSTALLED_APPS に以下の設定を追加してください。

    INSTALLED_APPS = (
        ....
        "guess",
        ...
    )
    

  • Google Analytics の VIEWID と、credentials.json の配置先を追加
    settings.py に VIEWID と、Service Accounts  |  Google Earth Engine API  |  Google Developers credentials.json の 配置先を追加してください。
    PREFETCH_CONFIG回線ごとの遷移確率の閾値になり、COMMAND_CONFIG値は、Django Command 内で使用する閾値になります。

GUESS_SETTINGS = {      
    "VIEW_ID" : "103185238",
    "CREDENTIALS" : "/home/user_name/credentials.json",
    "PREFETCH_CONFIG": {'4g': 15, '3g': 30, '2g': 45, 'slow-2g': 60},
    "COMMAND_CONFIG" : {'RATIO_TO_EVALUATE': 0.20, 'LOWER_LIMIT_TRANSITION_PROBABILITY': 0.05}
}

  • マイグレーションの実行
    migrate を実行すると、guess_guessresult テーブルが作成されます。
    これは、Google Analytics のデータ解析結果を格納するテーブルになります。

    python3.6 manage.py migrate
    

  • コマンドを実行
    store_ga実行すると、Google Analytics のデータ解析結果を登録します。

    python3.6 manage.py store_ga       
    

  • スケジュール実行
    kraiz/django-crontab: dead simple crontab powered job scheduling for django.使用しているのであれば、以下のような記述で スケジュール実行ができます。

    CRONJOBS = [
        ('30 01 * * *', 'django.core.management.call_command', ['store_ga'], {}, '>> /var/log/store_ga.log'),
    ]
    
    勿論、crontab に定義してスケジュール実行も可能です。

  • テンプレートタグを追加する
    テンプレートの head タグ内に以下の記述を追加します。
    これで、Google Analytics のデータ解析結果を元に <link rel="prefetch">追加されます。

    {% load guess %}       
    {% ifinstalled guess %}
        {% prefetch request.path %}
    {% endifinstalled %}
    


Django REST Framework (DRF) support

Django template 以外の Frontend からも使用できるように Web API を提供しています。

  • すべてのビューを含める
    urls.py に以下の記述を追加してください。

    from django.conf.urls import url, include   
    urlpatterns += [
        url('apis/', include('guess.urls')),
    ]
    
    上記の設定で、URL <your_domain >/apis/guessresult/<pagepath >/<effectivetype > APIへアクセスできます。

  • クライアント側の実装
    以下のJavaScript関数をクライアント側に実装します。
    Web API を呼び出し、その戻り値を元に、次に遷移する確率が高いページを、prefetchタグを使って先読みします。
    '/apis/guessresult/'部分はurls.py記述に合わせて調整する必要があります。

    function prefetch(url) {
        console.log(url);
        let hint = document.createElement('link');
        hint.rel = 'prefetch';
        hint.href = url;
        hint.as = 'html';
        hint.crossorigin = 'use-credentials';
        document.head.appendChild(hint);
    }
    function getConnection() {
        if (!window || !window.navigator || !window.navigator.connection) {
          return '3g';
        }
        return window.navigator.connection.effectiveType || '3g';
    }
    
    function guessNextPages() {
        var xhr = new XMLHttpRequest();
        // ハンドラの登録.
        xhr.onreadystatechange = function() {
            switch ( xhr.readyState ) {
                case 4: // データ受信完了.
                    if( xhr.status == 200 ) {
                        var data = xhr.responseText;
                        var jsonData = JSON.parse(data);
                        for (var i = 0; i < jsonData.length; i++) {
                            prefetch(location.origin + jsonData[i]['page_path']);
                        }
                    } else {
                        console.log("Response error! status=" + xhr.status);
                    }
                    break;
            }
        };
        var urlParam = encodeURIComponent(location.pathname);
        xhr.open( 'GET', '/apis/guessresult/' + urlParam + '/' + getConnection() + '/' + '?format=json', true );
        // POST 送信の場合は Content-Type は固定.
        xhr.setRequestHeader( 'Content-Type', 'application/json' );
        xhr.send(null);
    }
    
    上記、guessNextPages() を 'DOMContentLoaded' 等のタイミングで呼び出せば、先読みが行われます。
    window.addEventListener('DOMContentLoaded', function() {
      guessNextPages();
    })
    

  • HTTPサーバーの設定変更
    Web API のパラメータ <pagepath > にはスラッシュ(/) が含まれます。
    Apache 2.4 の場合は、AllowEncodedSlashes Virtual Host 設定内に追加します。

    AllowEncodedSlashes On     
    


先読みを行うページの決定方法

Guess.js の実装に忠実なわけではありませんが、以下のようなロジックで先読み対象のページを決定します。

  1. コマンドの実行した日から 180 日分の Google Analytics のデータを取得。 データ取得時以下のディメンション、メトリクスを指定。

    • ディメンション
      • ga:pagePath
      • ga:previousPagePath
    • メトリクス
      • ga:pageviews
      • ga:exits
  2. previousPagePath が (entrance)データを除外し、previousPagePath ごとの pagePath の 合計ページビューを計算。

  3. ページビュー多いページ 上位 20 % を抽出。 [1]:当サイトのアクセス数を解析した結果、パレートの法則に従っていそうだったので、上位 20 % としています。
  4. 3.抽出結果から、10% 以上の確率で遷移が発生したページのみを抽出。
  5. 4.抽出結果を テーブルに登録する。

TODO

  • 解析データの取得期間、閾値をコントロールできるようにする。
  • 別の計算方式で先読み対象のページを特定できるようにする。

感想

以下、実装した感想を記載します。

  • Google Analytics のデータの単純集計なので、実装はそこまで大変ではなかった。
  • Pandas で実装したが、Pandas を使用せずとも組める。パフォーマンス的には使わない方が速度は出そうに思った。
  • Java で 似たようなライブラリがあると、仕事にも適用できる。

以上です。

コメント