Mac版Kindleの蔵書一覧情報をPythonで読む
はじめに
Mac用のKindeはsqliteデータベースで蔵書情報を管理しており、各種DBクライアントツールや、Pythonなどのプログラミング言語で簡単にアクセスできます。
今回PythonでMac版Kindleの蔵書情報を読み取ってみました!
Pythonでの読み方
データベースは以下の場所に置いてあります。
~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite次のコードで購入した書籍のタイトルや著者、出版日、購入日などの情報を取得できます。
import pandas as pd
import sqlite3
from pathlib import Path
metadata_path = Path.home()/"Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite"
conn = sqlite3.connect(metadata_path)
book_df = pd.read_sql_query('SELECT * FROM ZBOOK', conn)
書籍タイトルや出版社などそのまま読み込める情報が多いですが、一部のカラムはバイナリ化されていて中身がよくわかりません。
例えば、著者情報が入っていると思われる「ZDISPAYAUTHOR」カラムは、バイナリ化されていて内容がわかりません。
特殊なカラム
一方、ZSYNCMETADATAATTRIBUTESという項目は、plist(プロパティリスト)という形式で入っているらしくどうやら読み込むことができるようです。ChatGPTに聞いて初めて知りました。
こんな形でbplist00から始まるバイナリデータとして入っています。
b'bplist00\xd4\x01\x02\x03\x04\x05\x06...これをPythonのplistlibというライブラリで読むことができました。
ただ、plistlibで読み込むだけだとまだ独特な構造をしていて扱いづらい状態なのでplistlibで読み込んだあと適宜展開する関数を作成しました
import plistlib
from typing import Any, Dict
def resolve_ns_keyed_archive_fully(data: bytes) -> Any:
if pd.isna(data):
return np.nan
root = plistlib.loads(data)
objects = root["$objects"]
top_uid = root["$top"]["root"]
# クラスID → クラス名
class_map = {
idx: obj["$classname"]
for idx, obj in enumerate(objects)
if isinstance(obj, dict) and "$classname" in obj
}
def resolve(obj: Any, memo: Dict[int, Any]) -> Any:
if isinstance(obj, plistlib.UID):
idx = obj.data
if idx in memo:
return memo[idx]
raw = objects[idx]
resolved = resolve(raw, memo)
memo[idx] = resolved
return resolved
elif isinstance(obj, list):
return [resolve(item, memo) for item in obj]
elif isinstance(obj, dict):
# クラスID に基づいて判定
class_id = obj.get("$class")
class_name = class_map.get(class_id.data) if isinstance(class_id, plistlib.UID) else None
# NSMutableArray / NSArray の展開
if class_name in ("NSMutableArray", "NSArray") and "NS.objects" in obj:
return resolve(obj["NS.objects"], memo)
# NSMutableDictionary / NSDictionary の展開
if class_name in ("NSMutableDictionary", "NSDictionary") and "NS.keys" in obj and "NS.objects" in obj:
keys = resolve(obj["NS.keys"], memo)
vals = resolve(obj["NS.objects"], memo)
return dict(zip(keys, vals))
# 通常の辞書展開
return {
resolve(k, memo): resolve(v, memo)
for k, v in obj.items()
if not (isinstance(k, str) and k.startswith("$"))
}
else:
return obj
return resolve(top_uid, {})展開したZSYNCMETADATAATTRIBUTESの中身はこんな感じです。
{'attributes': {'content_tags': {'tag': 'MANGA'},
'ASIN': 'B0F335Q25D',
'authors': {'author': '松井優征'},
'content_type': 'application/x-mobipocket-ebook',
'content_size': '166021120',
'publishers': {'publisher': '集英社'},
'title': '逃げ上手の若君 20 (ジャンプコミックスDIGITAL)',
'cde_contenttype': 'EBOK',
'origins': {'origin': {'type': 'Purchase', 'id': 'XXX-XXXXXX-XXXXXXX'}},
'purchase_date': '2025-05-01T15:08:44+0000',
'publication_date': '2025-05-02T00:00:00+0000'}}他のカラムと重複する情報も含まれています。
ここから著者や購入日時などの情報を取れそうです。
Colab実行手順
上記のコードをまとめてColabで中身を確認できるコードを公開しました
簡単な可視化例も載せています。
①ファイルの確認
ターミナルで以下のコマンドを実行してください。
cd ~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/
open .すると、Finderでフォルダが開かれBookData.sqliteの存在が確認できます。
②Colabを開いてにファイルをアップロード
次のリンクから「Open in Colab」をクリックし、notebookを開きます。
BookData.sqliteをColabのファイルタブからアップロードします。

あとは「ランタイム→すべてのセルを実行」でOK!
結果サンプル

セールの時とかにまとめ買いして購入冊数多くなってる月がある感じです

COMICはインディーズ漫画についてることが多かったですが商業出版のにもついてることがあったのでいまいち基準がわからず。
それにしても圧倒的に漫画が多い。。。


おわりに
今回使ったカラム以外にも有用そうな情報がいくつかありそうです。
また、今回は「ZBOOK」というテーブルだけ見たのですが、他にも漫画のシリーズ情報やコレクション情報を持つテーブルもあるので、余力あれば紹介していければと思います。
