見出し画像

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!

結果サンプル

2018年から書籍は基本Kindleで買うようにしました。
セールの時とかにまとめ買いして購入冊数多くなってる月がある感じです
確認できた限りタグはMANGAとCOMICSの2つ。漫画以外にはタグは付いてなさそうです。
COMICはインディーズ漫画についてることが多かったですが商業出版のにもついてることがあったのでいまいち基準がわからず。
それにしても圧倒的に漫画が多い。。。
荒川弘先生の作品はどれも面白いです!
講談社大好きマンになってます

おわりに

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


いいなと思ったら応援しよう!