【Django】GoogleアナリティクスAPIを使ってサイト内のトレンド(人気)を表示させる機能を追加


投稿日 2019年10月25日 >> 更新日 2023年3月2日

今回は、Djangoアプリケーション内でGoogleアナリティクスAPIを構築しアナリティクスに紐づいているWebサイトのトレンドコンテンツを表示させていきたいと思います。

トレンドなので、一定の時間に差し掛かったら更新されるようにカスタムコマンドも作成していきます。

ここで行う作業をそのまま本番稼働中のWebサイトで構築することも可能だと思います。

そしてこの作業をするにあたって準備をしておくことが、Analytics Reporting API(GoogleアナリティクスAPI)の初期設定です。

他にもAPIの基本的な操作やこの記事で出てくる「カスタムコマンド」での割と詳しい内容を添えておきますので宜しければそちらもご参考にしてください。

Analytics Reporting APIの初期設定

APIの基本的な操作

カスタムコマンドの概要

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.8
pip 9.0.1
使用ライブラリ ライセンス
Django==2.2.6 BSD
google-api-python-client==1.7.11 Apache Software
oauth2client==4.1.3 Apache Software

必要なライブラリと秘密鍵ファイルの確認

仮想環境下で実行する方もいらっしゃると思うので、インストール漏れが無いか確認します。

APIを使うには以下のライブラリが必要です。


$ pip3 install google-api-python-client==1.7.11

$ pip3 install oauth2client==4.1.3

そして秘密鍵である「client_secrets.json」ファイルです。

このファイルはDjangoプロジェクト内に配置するので分かる場所に置いておきましょう。

Articleアプリを作成

プロジェクトの作成


$ django-admin startproject project

プロジェクトディレクトリに移動しArticleアプリを作成


$ cd project

$ python3 manage.py startapp article

プロジェクトディレクトリの設定ファイルを編集

# project/project/settings.py

INSTALLED_APPS = [
    'article.apps.ArticleConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

Googleアナリティクスのデータを一時的に保存するためのモデルを定義します。

アプリディレクトリ内のモデルを編集

# project/article/models.py

from django.db import models


class Popular(models.Model):
    title = models.CharField('人気記事', max_length=100)
    path = models.CharField('URL', max_length=100)
    view = models.IntegerField('閲覧数')

    def __str__(self):
        return self.title

トレンドのコンテンツとして取得してくるデータはブログの記事です。

記事のタイトル、記事のURL、記事の閲覧数をデータベースに保存します。

admin管理画面の設定です。

# project/article/admin.py

from django.contrib import admin
from .models import Popular


admin.site.register(Popular)

ここで一旦モデルを反映させ管理者権限を作りアクセスします。

# project/

$ python3 manage.py makemigrations

$ python3 manage.py migrate

$ python3 manage.py createsuperuser

$ python3 manage.py runserver
127.0.0.1:8000

カスタムコマンドの作成

カスタムコマンド用に「management」ディレクトリと「commands」ディレクトリを作り、APIを呼び出すファイル「analytics_api.py」とAPIを実行させるファイル「populars.py」を作成します。

# project/article

article
    |--management
    |       |--commands
    |               |--analytics_api.py
    |               |--populars.py
    |
    |--migrations
    |--models.py
    |--admin.py

APIを呼び出すファイルの作成

VIEW_IDを定義した上、アナリティクスとWebサイトが紐づいているデータ内の「記事タイトル」「記事パス(URL)」「記事閲覧数(VIEW)」を取得します。

# project/article/management/commands/analytics_api.py

from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials


SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
KEY_FILE_LOCATION = 'client_secrets.json'
VIEW_ID = 'VIEWのID'

def initialize_analyticsreporting():
    credentials = ServiceAccountCredentials.from_json_keyfile_name(
                                    KEY_FILE_LOCATION,
                                    SCOPES
                            )
    analytics = build('analyticsreporting', 'v4', credentials=credentials)
    return analytics


def get_report(analytics):
    return analytics.reports().batchGet(
            body={
            'reportRequests': [{
                'viewId': VIEW_ID,
                'pageSize': 10,
                'dateRanges': [{"startDate": "7daysAgo", "endDate": "today"}],
                'dimensions': [{'name': 'ga:pagePath'}, {'name': 'ga:pageTitle'}],
                'dimensionFilterClauses': [{'filters': [{'dimensionName': 'ga:pagePath',
                                                       'expressions': ['/blogs/detail/']}]
                                           }],
                'metrics': [{'expression': 'ga:pageviews'}],
                'orderBys': [{'fieldName': 'ga:pageviews', 'sortOrder': 'DESCENDING'}],
             }]
            }
            ).execute()


def print_response():
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)

    for row in response['reports'][0]['data']['rows']:
        row_path = 'https://zerofromlight.com' + row['dimensions'][0]
        row_title = row['dimensions'][1]
        row_view = row['metrics'][0]['values'][0]
        yield row_title, row_path, row_view

「def get_report(analytics)」でタイトル・ドメイン無しのパス(URL)・閲覧数を取得し、「def print_response()」によってjson形式のデータをひとつずつ取り出しています。

それぞれ見ていきましょう。

# project/article/management/commands/analytics_api.py

def get_report(analytics):
    return analytics.reports().batchGet(
            body={
            'reportRequests': [{
                'viewId': VIEW_ID,
                'pageSize': 10,
                'dateRanges': [{"startDate": "7daysAgo", "endDate": "today"}],
                'dimensions': [{'name': 'ga:pagePath'}, {'name': 'ga:pageTitle'}],
                'dimensionFilterClauses': [{'filters': [{'dimensionName': 'ga:pagePath',
                                                       'expressions': ['/blogs/detail/']}]
                                           }],
                'metrics': [{'expression': 'ga:pageviews'}],
                'orderBys': [{'fieldName': 'ga:pageviews', 'sortOrder': 'DESCENDING'}],
             }]
            }
            ).execute()

pageSizeで上位10件分取得

dateRangesで日付、今日から過去7日間

dimensionsでは記事のパスと記事のタイトル、そしてdimensionFilterClausesで記事パスを制限しています。

もしdimensionFiterClausesを使わなかったら、すべてのパス(URL)を取得してしまうので、記事のある「/blogs//detail/key」のパスへ絞るようにしています。

metricsは閲覧数

orderBysはmetricsで指定した閲覧数を降順で取得してくるように設定しています。

続いてdef print_response()内での定義です。

# project/article/management/commands/analytics_api.py

def print_response():
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)

    for row in response['reports'][0]['data']['rows']:
        row_path = 'https://zerofromlight.com' + row['dimensions'][0]
        row_title = row['dimensions'][1]
        row_view = row['metrics'][0]['values'][0]
        yield row_title, row_path, row_view

row_pathの変数では、「'https://zerofromlight.com/blogs/detail/key/'」と出力されるようにルートドメインをハードコードしています。

このrow_pathは後にHTMLのaタグに渡します。

そして格納された変数を「yield」で返しています。

「yield」は1つずつ処理をしてくれる文です。よってジェネレータオブジェクトとして返されるのでfor文で回すと下図のようになります。


for title, path, view in print_response():
    print(title, path, view)

もし「yield」文ではなく「return」文で返すと単一の値しか返されません。


def print_response():
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)

    for row in response['reports'][0]['data']['rows']:
        row_path = 'https://zerofromlight.com' + row['dimensions'][0]
        row_title = row['dimensions'][1]
        row_view = row['metrics'][0]['values'][0]
        return row_title, row_path, row_view   \# 変更


"""出力"""
print_response()

他にデータを取得する方法といえば、空リストの変数へ格納すると言った事が考えられます。

ただ記述するコード量が増えるため「yield」文はかなりシンブルな機能を果たしてくれます。

APIを実行するファイルの作成

ここでは実行するコマンド名に当たる「populars.py」を作成します。

populars.pyが実行されると、データベースを初期化しAPIから取得したデータをデータベースに格納します。

# project/article/management/commands/populars.py

from django.core.management.base import BaseCommand
from article.models import Popular
from .analytics_api import print_response


class Command(BaseCommand):

    def handle(self, *args, **options):
        Popular.objects.all().delete()

        for title, path, view in print_response():
            Popular.objects.create(
                        title=title, path=path, view=view
            )

        self.stdout.write(self.style.SUCCESS('作成完了'))

秘密鍵「client_secrets.json」の設置

秘密鍵はプロジェクトディレクトリ内に設置します。

# project/

project
    |--article
    |--project
    |--client_secrets.json   # 設置
    |--db.sqlite3
    |--manage.py

ではターミナルを新しく開いてpopularsコマンドを実行しましょう。

# project/

$ python3 manage.py populars
作成完了

作成完了されたら、admin管理画面にて確認してみます。

URLと閲覧数もしっかり保存されています。

※URLとパスは同一

人気記事を表示させる

プロジェクトディレクトリ内の「urls.py」を編集します。

# project/project/urls.py

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('article.urls')),
]

アプリディレクトリ内に新しく「urls.py」を作成します。

# project/article/urls.py

from django.urls import path
from . import views


app_name = 'article'

urlpatterns = [
        path('', views.index, name='index'),
]

同じディレクトリ内の「views.py」を編集します。

# project/article/views.py

from django.shortcuts import render
from .models import Popular


def index(request):
    article = Popular.objects.all()
    return render(request, 'article/index.html',
                                    {'article':article})

アプリディレクトリ内に新しく「templates」ディレクトリを作成し、その中に「article」ディレクトリを作成し、articleディレクトリ内で「index.html」を作成します。

# project/article/

article
    |--management
    |--migrations
    |--templates
            |--article
                  |--index.html
<!-- project/article/templates/article/index.html -->

<!doctype html>
<html>
    <head>

    <title>人気記事</title>

    </head>
    <body>

        <div align="center">
            <h1>人気記事トップ10</h1>
            {% for article in article %}
                <p>
                    <a href="{{ article.path }}">{{ article.title }}</a>
                    -{{ article.view }}-aタグの中身({{ article.path }})
                </p>
            {% endfor %}
        </div>

    </body>
</html>

aタグの中身は上図にあるとおり大本サイトのフルパス(URL)を当てはめています。

俗に言う内部リンクとなりますが、「analytics_api.py」を編集することでキーだけを取り出したりすることもできます。

# project/article/management/commands/analytics_api.py

.......
.......

def print_response():
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)

    for row in response['reports'][0]['data']['rows']:
        row_path = row['dimensions'][0].split('/')[3]   # 編集
        row_title = row['dimensions'][1]
        row_view = row['metrics'][0]['values'][0]
        yield row_title, row_path, row_view

特定のパス(URL)を取得したい場合は、単純にスライスを使って指定します。

# project/article/management/commands/analytics_api.py

.......
.......

def print_response():
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)

    for row in response['reports'][0]['data']['rows']:
        row_path = row['dimensions'][0][6:]   # 編集
        row_title = row['dimensions'][1]
        row_view = row['metrics'][0]['values'][0]
        yield row_title, row_path, row_view

人気記事を表示できたところで、最後にデザインの変更とカスタムコマンドの定期実行をしていきたいと思います。

アプリのデザインを変更

CSSが適用されるように、それぞれの要素にブロッグタグを当てはめます。

# project/article/templates/article/index.html

<!doctype html>
<html>
    <head>

        <title>人気記事</title>

    </head>
    <body>

        <div align="center">
            <div class="card">
                <div class="card-title"><h1>人気記事トップ10</h1></div>
                <div class="card-content">
                    {% for article in article %}
                        <p class="card-text">
                            <a class="a-link" href="{{ article.path }}">{{ article.title }}</a>
                            - <text class="p-view">{{ article.view }}</text>
                        </p>
                    {% endfor %}
                </div>
            </div>
        </div>

    </body>
</html>

headタグ内にstyleタグを設けてCSSを記述していきます。

<!-- project/article/templates/article/index.html -->

<!doctype html>
<html>
    <head>

        <title>人気記事</title>

        <style>
            body {
                background: gray;
            }
            .card {
                width: 350px;
                box-shadow: 0 2px 5px \#ccc;
            }
            .card-title {
                padding: 10px;
                color: white;
                background: hotpink;
            }
            .card-content {
                padding: 10px;
                background: gray;
            }
            .a-link {
                font-size: 16;
                color: white;
            }
            .p-view {
                color: red;
            }
        </style>

    </head>
    <body>

        ....
        ....

    </body>
</html>

リロードして表示すると

カスタムコマンドの定期実行

ここでは簡単に進めてしまうので、仮想環境などの構築をされていたらこちらを参考にしながら実行してみてください。

ではcrontabをコピーし、ファイル名を拡張子無しの「cron_test」として保存します。

# home/ubuntu/

$ sudo cp /etc/crontab /etc/cron.d/cron_test

crontabファイル内へ実行命令を記述することもできますが、非推奨となっているのでコピーして「cron.d」ディレクトリ内へ配置しました。

cron_testファイルをvimエディタで開いて記述します。

# home/ubuntu/

$ sudo vi /etc/cron.d/cron_test

ルートディレクトリから実行されるので、Djangoのプロジェクトディレクトリまでのフルパスを記述します。(通常の環境)

時間は8時・12時・15時・18時・21・0時に実行されるよう設定します。

# home/ubuntu/etc/cron.d/cron_test

00  8,12,15,18,21,0  *  *  *  root python3 /home/ubuntu/project/manage.py populars

vimエディタを閉じるには、キーボード左上の「Esc」を押し「:」に続けて「wq」とすることで保存され終了します。

設定を反映します。

# home/ubuntu/

$ sudo service cron restart
 * Restarting periodic command scheduler cron
 * Stopping periodic command scheduler cron       [  OK  ]
 * Starting periodic command scheduler cron         [  OK  ]

これで自動化の完了です。

参考

今回非常に参考させていただいサイトはこちらになります。

それでは以上となります。

最後までご覧いただきありがとうございました。