PythonでFirebaseを操作する

Python

FirebaseはGoogleが運営するBaaSです。これを使ってバーコード在庫管理WEBアプリを作成しました。詳しくはこちら。

アプリで使用するデータはFirebaseの機能であるCloud FirestoreというNoSQLデータベースに保存されています。Cloud FirestoreはWEBアプリや管理コンソールで操作、表示できますが、Excelの呪縛にとらわれ続ける我々はどうしてもExcelファイルとして取り出さなければならないのです!

そこで、PythonでFirebaseを操作できるfirebase-adminモジュールを使って、この任務を完遂します。

認証情報を設定する

Firebaseを外部から操作するには、それが正規ユーザーの操作であることを証明する必要があります。WEBアプリの場合はAPIキー等でそれをやっていますが、Pythonの場合は秘密鍵(超絶長いパスワード的なものが記録されたファイル)を使います。

WEBアプリからはセキュリティルールに則りデータベースにアクセス制限がかかりますが、Pythonで使う秘密鍵では管理権限となりルールに関係なくデータベースにアクセス可能です。

秘密鍵は次の手順で入手します。

1.プロジェクト画面のプロジェクトの設定を開きます。

2.サービスアカウントタグにある新しい秘密鍵の生成ボタンを押します。

3.ポップアップでキーを生成ボタンを押すとJSONファイルがダウンロードされますので、安全なところへ保存します。

このファイルが第三者に漏洩するとFirebaseのほぼすべての操作権限をわたすことになりますので取り扱いには注意してください。

秘密鍵を直接スクリプトから読み込むこともできますが、Googleが推奨しているのは環境変数にする方法です。なので、環境変数にしておきます。

設定は結構面倒なので、単発使用やちょっと試すだけの場合は次からの手順は飛ばして後述の解説パートの秘密鍵ファイル直読み方式をご利用ください。

4.Windowsの検索でシステム環境変数と検索します。システム環境変数の編集が候補で表示されるのでそれを開きます。

5.システムのプロパティが開くので下の方の環境変数ボタンを押します。

6.環境変数ウィンドウが開くので下側のシステム環境変数新規ボタンを押します。

7.変数の設定ウィンドウが開くので変数名に
GOOGLE_APPLICATION_CREDENTIALS
と入力します。
※GOOGLE_APPLICATION_CREDENTIALSは他のGoogleCloudアプリからも認証時に引く変数のようなので、すでに作成してある場合はパスを追加すればいいと思います(未確認)。

8.ファイルの参照ボタンで3.で保存したファイルを選択します。変数値にファイルパスが入ります。

9.OKボタンで確定します。

これで秘密鍵を環境変数に設定できました。

Python用モジュールをインストールする

PythonスクリプトからFirebaseを操作するのに必要なモジュールをインストールします。

pip install firebase-admin

ものすごい数の関連モジュールとともにfirebase-adminがインストールされます。

これでPythonからFirebaseを操作できるようになりました。firebase-adminでFirebaseの機能はほぼほぼPythonからさわれるようになっていますが、一番使い道があるのはデータベースへのアクセスでしょう。ていうか他の機能をプログラムからさわるシチュエーションは限定的です(たとえばユーザーを一気に大量生成するとか)。通常は管理コンソールで事足ります。

PythonでCloud Firestoreからデータを取得する

スクリプトは単純です。在庫管理WEBアプリで作った次のようなコレクションがあります。

これを全部取得するには

import firebase_admin
from firebase_admin import firestore

firebase_admin.initialize_app()
db = firestore.client()

doc_ref = db.collection('food')
docs = doc_ref.stream()

for doc in docs:
    print(
        f"JAN:{doc.id} "
        f"商品名:{doc.get('product_name')} "
        f"在庫:{doc.get('stock')} "
        f"下限:{doc.get('lower')}"
        )

実行すると

全データを取得できました。

PythonでCloud Firestoreへデータを書き込む

新しく【JAN:4901990522731 商品名:赤いきつねうどん】をコレクションに追加したいです。

import firebase_admin
from firebase_admin import firestore

firebase_admin.initialize_app()
db = firestore.client()

jan = '4901990522731'
p_name = '赤いきつねうどん'
stock = 2
lower = 1

doc_ref = db.collection('food').document(jan)
doc_ref.set({
    'product_name': p_name,
    'stock': stock,
    'lower': lower
})

実行すると

追加されました。

解説

しくみはシンプルで

1.Firebaseを初期化
2.データベースクライアント(FirestoreClient)インスタンスを取得
3.クライアントが持つメソッドを呼んで操作する

です。

firebase_admin.initialize_app()で先に設定した環境変数から認証情報を取得してFirebaseが初期化されます。

firestore.client()でデータベースを操作する権限を持つクライアント(FirestoreClient)を取得します。

あとはcollectionやdocumentで対象のデータを指定して、get、set、update、deleteなどのメソッドで操作します。

FirebaseのAPIリファレンスはJavaライクな構成で非常にわかりやすいので、公式を読むだけで使えるようになると思います(対してPythonはなんであんなにわかりずらいのか・・・)。

認証に環境変数を使わず、秘密鍵ファイルを直接読み込むようにする場合は

firebase_admin.initialize_app()

この初期化部分を

from firebase_admin import credentials

cred = credentials.Certificate('秘密鍵ファイルのパス')
firebase_admin.initialize_app(cred)

に置き換えてください。

Cloud FirestoreをExcelへ変換する

本題です。Firestoreから取得したデータをExcelファイルにします。しなければなりません。

データ取得スクリプトにOpenPyXlでExcelファイル出力処理を追加します。

import firebase_admin
from firebase_admin import firestore
import openpyxl as excel

firebase_admin.initialize_app()
db = firestore.client()

doc_ref = db.collection('food')
docs = doc_ref.stream()

wb = excel.Workbook()
ws = wb.active
header = ('JAN', '商品名', '在庫', '下限')
ws.append(header)

for doc in docs:
    ws.append((doc.id, doc.get('product_name'), doc.get('stock'), doc.get('lower')))

wb.save('food.xlsx')

実行すると、food.xlsxができて

データがExcelになりました。

注意点としてExcelにできるのはすべてのドキュメントが同じフィールドを持ったコレクションです。

Cloud FirestoreへExcelからデータを投入する

逆も簡単です。データ数が多い方が面白いので過去ネタで使用した架空個人情報100件が入力されている次のようなシートでやりましょう。

ソースコードは

import sys
import firebase_admin
from firebase_admin import firestore
import openpyxl as excel

firebase_admin.initialize_app()
db = firestore.client()
fp = sys.argv[1]

wb = excel.open(fp)
ws = wb.active

for r in ws.iter_rows(min_row=2):
    name = r[0].value
    kana = r[1].value
    addr1 = r[2].value
    addr2 = r[3].value
    addr3 = r[4].value
    file_path = r[5].value
    cid = str(r[6].value)
    status = r[7].value

    doc_ref = db.collection('customer').document(cid)
    doc_ref.set({
        'name': name,
        'kana': kana,
        'addr1': addr1,
        'addr2': addr2,
        'addr3': addr3,
        'file_path': file_path,
        'status': status
    })

このスクリプトにExcelファイルをD&Dすると

Firestoreに新しいコレクションができドキュメントが100件追加されました。もし同名のコレクション、ドキュメントがすでにある場合は上書きされます。

処理時間は体感で2~3秒といったところでしょうか。

このようにExcelシートで表現できる構造のドキュメントであれば、大量に投入する必要があるときにPython+Excelでかなりの省力化ができます。

フィールド名をハードコーディングではなく、Excelのタイトル列から持ってくるようにすれば使い回せるようになりますね。

まとめ

Excelからのデータ投入は初期データを与えるときに使えますね。Excelを使えるようにしといてよかった!めでたしめでたし。別にExcelである必要はなくCSVでいいのではないかというツッコミは聞こえません。

おわり。