見出し画像

PYTHONではじめてのWEBサイトを作成 6

さてはじめてWEbサイトを作っているわけですが、通常の仕事もやっているのでなかなか進みません。週末も開発するはずでしたが母が骨折してしまい実家に行って庭仕事を手伝っていました。

作ろうとしているサイトはこのような感じです。

トップページで近くのバインミーを探す
バインミー店のランキング
バインミー店の店舗情報
バインミーの傾向
SNSからバインミーのニュースを取得
広告設置
UberEatsのリンク(アフィリエートにできるか)

で、本日は

Google Place APIでバインミーの店を取得
バインミー店のデータベースをSQliteで生成
バインミーの店のリストをWEBにリスト表示

さあいってみましょう!


Google Places APIから情報を取得

Google Places APIをアクティブ化

  1. Google Cloud Consoleにアクセスして、プロジェクト「banhmilove」を選択。

  2. APIとサービスライブラリ から Places API を検索し、有効化します。

  3. 認証情報 セクションで新しいAPIキーを生成し、適切な制限(HTTPリファラ、IPアドレスなど)を設定します。

PlacesAPI


データベースを作成


ちょっと復習

  • models.py:モデルはデータベースの構造を定義します。各モデルはデータベーステーブルに対応します。

  • views.py:リクエストを処理し、レスポンスを返します。テンプレートと連携してHTMLを生成します。

  • urls.py:URLパターン(テンプレート)と対応するビューをマッピングします。各アプリケーションは自身のurls.pyを持ち、プロジェクト全体のurls.pyでまとめられます。

ざっくりとmodels.pyでデータを定義してviews.pyで処理してからurls.pyでtemplatew表示するというような流れです。
またDjangoでは、モデルを定義すればデータベースを意識せずにマッピングされ管理されます。これは「オブジェクトリレーショナルマッピング(ORM)」と呼ばれ、モデルの定義からデータベースのテーブル、カラム、リレーションシップが自動的に作成される仕組みです。

Django開発環境はデフォルトのSQliteで

SQLiteは軽量で設定が不要なデータベースシステムであり、小規模なプロジェクトや開発・テスト環境に適しています。modelsの設定をすればあまり意識しなくて良いようです。本番環境はAWSでPostgreSQLにしようかと思っています。

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


modelsで店舗情報をStoreモデルの定義

from django.db import models

class Store(models.Model):
    name = models.CharField(max_length=255)  # 店名
    prefecture = models.CharField(max_length=100)  # 都道府県
    city = models.CharField(max_length=100, default="Tokyo")  # 市区町村
    website = models.URLField(blank=True, null=True)  # ウェブサイト
    rating = models.FloatField()  # レーティング
    review_count = models.IntegerField()  # レビュー数
    latitude = models.FloatField()  # 緯度
    longitude = models.FloatField()  # 経度
    place_id = models.CharField(max_length=255, unique=True)  # Google Place ID

    def __str__(self):
        return self.name

classのfieldで店名、都道府県名、市区町村、ウェブサイト、レーティング、レビュー数、緯度、軽度、placeIDを取得します。

マイグレーションの作成

マイグレーションはモデルの変更をデータベースに適用するための指令書のようなもので、Djangoは makemigrations コマンドを使ってこれを自動生成します。modelsを更新するときには必ず行います。
migrationすると自動的にプロジェクトの配下にmigrationsフォルダができます。更新するごとにログが残る仕組みです。

python manage.py makemigrations

マイグレーションの適用

マイグレーションファイルが作成された後、これをデータベースに適用することで、モデルの構造がデータベースに反映されます。これは migrate コマンドで行います。

python manage.py migrate

これでバインミーショップのデータを入れる箱ができたので次は実際にバインミーショップのデータを取得。

Google Places APIデータを実際に取得する

本番時には定期的に全国のバインミーショップを取得するが今回は一時的にGooglePlaces APIから東京のバインミーショップを取得します。

Djangoコマンドの作成と配置

Djangoでは、カスタムコマンドを作成するためには特定のディレクトリ構造を守る必要があります。

  • management/: このディレクトリは各アプリケーションディレクトリに含まれ、カスタムコマンド関連のファイルが保存されます。

  • commands/: managementディレクトリ内にこのディレクトリを作成し、ここに個々のカスタムコマンドのPythonスクリプトを配置します。

  • fetch_banhmi.py: このファイルはカスタムコマンドを実装するPythonスクリプトです。ファイル名がコマンド名としてmanage.pyから呼び出されます。

コマンドを実行するには、ターミナルから以下のように入力します。

python manage.py fetch_banhmi

本番時にはcronジョブを使用して毎日または毎週特定の時間にコマンドを実行するよう設定する予定です。

banhmilove/banhmilove_app/management/commands/fetch_banhmi.py

これはdjango.core.management.base を継承したカスタムコマンドを作成し、定期的に全国のバインミー店を検索し、データベースに保存します。

from django.core.management.base import BaseCommand
import requests
from banhmilove_app.models import Store
from django.db import transaction

class Command(BaseCommand):
    help = 'Fetches Banh Mi shop data from Tokyo'

    def handle(self, *args, **options):
        self.stdout.write("Fetching Banh Mi shops in Tokyo...")
        self.fetch_places()

    def fetch_places(self):
        locations = ["Tokyo"]
        for location in locations:
            url = "https://maps.googleapis.com/maps/api/place/textsearch/json"
            params = {
                "query": f"Banh Mi shop in {location} Japan",
                "key": "yourappkey",  # 本番キーが公開されないように注意
                "language": "ja"
            }
            response = requests.get(url, params=params)
        self.stdout.write(f"Response from API: {response.text}")  # APIの応答内容をログに出力
        if response.status_code == 200:
            results = response.json().get('results', [])
            if not results:
                self.stdout.write("No data received from API.")
            else:
                    self.save_data(results, location)
                    self.stdout.write(f"Stores in {location} fetched and saved successfully.")
        else:
            self.stdout.write(f"Failed to fetch data for {location}: {response.status_code} - {response.text}")

    def save_data(self, results, location):
        with transaction.atomic():
            for place in results:
                website = self.fetch_place_details(place['place_id'])
                store = Store(
                    name=place.get('name'),
                    prefecture=location,
                    city="N/A",
                    url=website,
                    rating=place.get('rating', 0),
                    review_count=place.get('user_ratings_total', 0),
                    latitude=place.get('geometry', {}).get('location', {}).get('lat', 0),
                    longitude=place.get('geometry', {}).get('location', {}).get('lng', 0)
                )
                store.save()
    
    def fetch_place_details(self, place_id):
        detail_url = "https://maps.googleapis.com/maps/api/place/details/json"
        params = {
            "place_id": place_id,
            "fields": "website",
            "key": "yourappkey,  # 同様にAPIキーを安全に保管
            "language": "ja"
        }
        response = requests.get(detail_url, params=params)
        if response.status_code == 200:
            details = response.json().get('result', {})
            return details.get('website', '')
        return ''

そして

python manage.py fetch_banhmi

確認してみましょう。

確認の仕方は
ターミナルを開きプロジェクトのルートディレクトリでDjangoのインタラクティブシェルを起動

python manage.py shell

Pythonのインタラクティブモードになります。

from your_app_name.models import Store  # your_app_nameはあなたのアプリケーション名に置き換えてください
stores = Store.objects.all()

さらに

for store in stores:
    print(store.name, store.prefecture)

取得できました。

>>> stores = Store.objects.all()
>>> for store in stores:
...     print(store.name, store.prefecture)
... 
バインミー☆サンドイッチ Tokyo
バインミー☆サンドイッチ 水道橋東口店 Tokyo
バインミーシンチャオ Tokyo
バインミー シンチャオ 浅草店 Tokyo
nicoバインミー Tokyo
MIKAバインミー 早稲田店 (Mika bánh mì) Tokyo
BANH MI NGON NGON TAKADANOBABA Tokyo
EBISU BANH MI BAKERY Tokyo
BANH MI NGON NGON OKUBO(バインミーゴンゴン 大久保店) Tokyo
バインミーバイミー Tokyo
バインミーサンドイッチ神保町店 Tokyo
スタンドバインミー Tokyo
バインミー☆サンドイッチ Tokyo
バインミーバーバー下北沢店 Tokyo
Bánh Mì Xin Chào Tokyo
ベトナムガーデン Tokyo
BANH MI XIN CHAO NIHONBASHI バインミーシンチャオ日本橋店 Tokyo
バインミーシンチャオ 中野店 Tokyo
バインミーバーバー中目黒 Tokyo
ベトナムサンドイッチバインミー中野店 Tokyo
バインミー☆サンドイッチ Tokyo
バインミー☆サンドイッチ 水道橋東口店 Tokyo
バインミーシンチャオ Tokyo
バインミー シンチャオ 浅草店 Tokyo
nicoバインミー Tokyo
MIKAバインミー 早稲田店 (Mika bánh mì) Tokyo
BANH MI NGON NGON TAKADANOBABA Tokyo
EBISU BANH MI BAKERY Tokyo
BANH MI NGON NGON OKUBO(バインミーゴンゴン 大久保店) Tokyo
バインミーバイミー Tokyo
バインミーサンドイッチ神保町店 Tokyo
スタンドバインミー Tokyo
バインミー☆サンドイッチ Tokyo
ベトナミーズトーキョー東京大手町本店 Tokyo
バインミーバーバー下北沢店 Tokyo
Bánh Mì Xin Chào Tokyo
BANH MI XIN CHAO NIHONBASHI バインミーシンチャオ日本橋店 Tokyo
バインミーバーバー中目黒 Tokyo
バインミーシンチャオ 中野店 Tokyo
ベトナムサンドイッチバインミー中野店 Tokyo


Google Cloud Consoleで使用されているかチェック

コンソールのmetricsで確認すると使用されています。


API metrics


Djangoの管理画面でもデータをチェック

Djangoの特徴として初心者でも簡単にデータベースにデータが追加、削除、編集ができる管理画面がついています。

これでバインミーのショップがデータ登録しているかチェックしましょう。またデータの表示、追加、編集ができるように設定しておきます。

管理画面の有効化

デフォルトで管理画面の設定はできているはずですが確認していきましょう。

settings.pyで以下を確認します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 他のアプリケーション
]

Djangoのadminが設定されていれば大丈夫です。

Urls.pyにadminのURLパターンがあるか確認

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

urlpatterns = [
    path('admin/', admin.site.urls),
    # 他のURLパターン
]

モデルの管理画面への登録

Djangoの管理画面で特定のモデルを管理するには、そのモデルを管理画面に登録する必要があります。これはadmin.pyファイルで行います。たとえば、あなたが作成したStoreモデルを管理画面で扱いたい場合は、該当するアプリケーションのadmin.pyに以下のように記述します:

admin.py

from django.contrib import admin
from .models import Store

admin.site.register(Store)


スーパーユーザーの作成

管理画面にアクセスし、データベースの操作を行うためには、スーパーユーザーアカウントを作成する必要があります。コマンドラインから以下のコマンドを実行してスーパーユーザーを作成します。

python manage.py createsuperuser


管理画面へのアクセス

すべての設定が完了したら、ブラウザを開き、通常は http://127.0.0.1:8000/admin/ にアクセスすることで管理画面にログインできます。スーパーユーザーのユーザー名とパスワードを使用してログインします。

  1. ユーザー名の入力: 最初に、ターミナルにユーザー名を入力するように求められます。これは新しいスーパーユーザーのユーザー名です。

  2. 電子メールの入力: 次に、メールアドレスを入力するように求められます。このメールアドレスはスーパーユーザーのものである必要はありませんが、重要な情報を取得できるため、実在するメールアドレスを入力することが一般的です。

  3. パスワードの入力: 最後に、新しいスーパーユーザーのパスワードを入力するように求められます。パスワードは表示されないので注意が必要です。パスワードは安全に設定する必要があります。

パスワードを入力した後、エンターキーを押すと、スーパーユーザーが作成されます。作成が成功した場合、以下のようなメッセージが表示されます。

Superuser created successfully.

ログインすると以下の管理メニューが表示されます。

django管理画面 


Storeを追加


バインミー店をページに表示

viewsでStoreのデータを取得してstore_list.htmlに返します。

views.html

def store_list(request):
    stores = Store.objects.all()  # データベースからすべての店舗データを取得
    return render(request, 'banhmilove_app/store_list.html', {'stores': stores})


htmlに以下のコードを書きます。

store_list.html

{% extends 'banhmilove_app/base.html' %}

{% block content %}
<div class="container">
    <h1>Store List</h1>
    <ul>
        {% for store in stores %}
        <li>
            <strong>{{ store.name }}</strong> - {{ store.prefecture }} {{ store.city }}<br>
            Rating: {{ store.rating }} | Reviews: {{ store.review_count }}<br>
            <a href="{{ store.url }}">Visit Website</a>
        </li>
        {% empty %}
        <li>No stores found.</li>
        {% endfor %}
    </ul>
</div>
{% endblock %}


店名、レビュー数、レーティング、URLが取得できました。
APIの無料の成約があるので今回は東京のリストを取得しました。


バインミーのリスト


まとめ

今回は、Google Places APIでバインミーのお店のリストを取得しました。
SQliteでデータベースを構築してHTMLに表示しました。

次はランキングページにチャレンジします。


最近忙しくてこれが進んでいません。

Iot関連やAI関連の相談が増えているので個人的にはかなり面白いです。


この記事が気に入ったらサポートをしてみませんか?