MacBook中ibook读书笔记导出
Go to file
douboer 4e3b8abc34 'update' 2025-08-15 17:20:30 +08:00
.vscode 'update' 2025-08-15 13:49:02 +08:00
__pycache__ 'update' 2025-08-15 17:20:30 +08:00
backup 'update' 2025-08-15 13:49:02 +08:00
data 'update' 2025-08-15 17:20:30 +08:00
examples 优化效率 2025-08-12 15:05:29 +08:00
export_notes 'update' 2025-08-15 17:20:30 +08:00
icons 'update' 2025-08-15 13:49:02 +08:00
.DS_Store 'update' 2025-08-15 13:49:02 +08:00
annotationdata.py 'update' 2025-08-15 17:20:30 +08:00
booklist_parse.py 'update' 2025-08-15 17:20:30 +08:00
config.py 'update' 2025-08-15 13:49:02 +08:00
detaildesign.md 'update' 2025-08-15 17:20:30 +08:00
exportbooknotes.py 'update' 2025-08-15 17:20:30 +08:00
kmanapp.png 'update' 2025-08-15 13:49:02 +08:00
kmanapp.qrc 'update' 2025-08-15 13:49:02 +08:00
mainwindow.ui 'update' 2025-08-15 13:49:02 +08:00
opf_parse.py 'update' 2025-08-15 17:20:30 +08:00
push.sh 'update' 2025-08-15 13:49:02 +08:00
readme.md Initial commit 2025-08-06 13:11:08 +08:00
test.ui 'update' 2025-08-15 13:49:02 +08:00
toc_parse.py 'update' 2025-08-15 17:20:30 +08:00

readme.md

epub结构

sqlite3 ~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite sqlite> .tables ZAEANNOTATION Z_METADATA Z_PRIMARYKEY
ZBCCLOUDSYNCVERSIONS Z_MODELCACHE

ZAEANNOTATION表:

字段名 含义描述推测
Z_PK 4662 可能是该条记录在数据库中的主键Primary Key用于唯一标识这条批注数据
Z_ENT 1 可能代表实体类型Entity Type用于区分数据库中不同类型的记录此处可能标识为“批注”类实体
Z_OPT 1 可能是版本号或优化字段Optimization用于记录数据的更新次数或状态标识
ZANNOTATIONDELETED 0 标识批注是否被删除0表示未删除1可能表示已删除
ZANNOTATIONISUNDERLINE 1 标识批注是否为下划线样式1表示该批注是下划线标注0可能表示非下划线
ZANNOTATIONSTYLE 0 可能表示批注的样式属性如颜色、粗细等0可能对应默认样式
ZANNOTATIONTYPE 2 表示批注的类型可能用于区分是下划线、高亮、注释等不同类型2此处可能对应下划线批注
ZPLABSOLUTEPHYSICALLOCATION 0 可能是批注在文档中的绝对物理位置信息,具体含义需结合文档存储格式判断
ZPLLOCATIONRANGEEND 0 批注内容在文档中的位置范围终点,可能是字符偏移量或段落索引等
ZPLLOCATIONRANGESTART 9 批注内容在文档中的位置范围起点,可能是字符偏移量或段落索引等,与终点配合确定批注选中的文本范围
ZANNOTATIONCREATIONDATE 774064827.1 批注的创建时间,可能是时间戳格式(需转换为具体日期时间)
ZANNOTATIONMODIFICATIONDATE 774064829.2 批注的最后修改时间,时间戳格式,记录批注内容或属性的更新时间
ZANNOTATIONASSETID 768E1CD0B3086166F791683869B12425 可能是该批注所属文档的唯一标识Asset ID用于关联批注对应的电子书或文档
ZANNOTATIONCREATORIDENTIFIER com~apple~iBooks 标识创建该批注的应用程序此处表明是苹果的iBooks应用创建的批注
ZANNOTATIONLOCATION epubcfi(/6/20[id155]!/4/412/1,:0,:97) 采用EPUB格式的CFIContent Fragment Identifier精确标识批注在电子书文档中的位置
ZANNOTATIONNOTE 批注的备注内容,此处为空表示该批注没有额外添加的文字备注
ZANNOTATIONREPRESENTATIVETEXT 在听完这个充满暴力、腐败和表里不一的悲剧故事之后,希维格和我都已精疲力尽。我们不知道到底谁比较可恶:是罪不可逭的席格和犹太人防卫联盟,还是恶意侵犯席格的宪法基本人权、并且否认曾许下承诺的政府官员。 可能是批注所关联的代表性文本,通常是包含批注选中内容的上下文文本
ZANNOTATIONSELECTEDTEXT 在听完这个充满暴力、腐败和表里不一的悲剧故事之后,希维格和我都已精疲力尽。我们不知道到底谁比较可恶:是罪不可逭的席格和犹太人防卫联盟,还是恶意侵犯席格的宪法基本人权、并且否认曾许下承诺的政府官员 被用户选中并添加批注(下划线)的具体文本内容,优先使用该字段作为内容定位依据
ZANNOTATIONUUID 7097EAE0-0EDB-4552-9AE0-FA6AB390B90B 批注的唯一标识符UUID用于在系统中唯一标识这条批注避免重复
ZFUTUREPROOFING1 预留字段,可能用于未来功能扩展,目前未使用
ZFUTUREPROOFING10 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING11 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING12 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING2 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING3 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING4 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING5 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING6 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING7 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING8 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING9 预留字段,用于未来功能扩展,目前未使用
ZPLSTORAGEUUID 774064829.2 可能是与存储相关的唯一标识符,用于关联批注在存储系统中的位置或状态
ZPLUSERDATA 可能用于存储与用户相关的自定义数据,此处为空表示无额外用户数据

笔记数据

书籍元数据

PLIST_PATH = os.path.expanduser("~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist")

{ "Books" => [

……

603 => {
  "artistName" => "(美)德肖维茨"
  "BKAllocatedSize" => 1388544
  "BKBookType" => "epub"
  "BKDisplayName" => "最好的辩护-德肖维茨"
  "BKGeneratedItemId" => "768E1CD0B3086166F791683869B12425"
  "BKGenerationCount" => 1
  "BKInsertionDate" => 773817769
  "BKPercentComplete" => 1
  "book-info" => {
    "package-file-hash" => "768E1CD0B3086166F791683869B12425"
  }
  "genre" => "法律"
  "isPreview" => 0
  "itemName" => "最好的辩护"
  "path" => "/Users/gavin/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents/最好的辩护.epub"
  "sourcePath" => "/Users/gavin/Library/Containers/com.apple.iBooksX/Data/Library/Caches/Inbox/最好的辩护-德肖维茨.epub"
  "updateDate" => 2025-07-10 05:22:49 +0000
}
……

]

笔记数据库

DB_PATH = os.path.expanduser("~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite")

books

~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite gavin@GavinsMAC BKLibrary % sqlite3 ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite sqlite> .tables ZBCCLOUDSYNCVERSIONS ZBKJALISCOSTATUS Z_MODELCACHE
ZBKCOLLECTION ZBKLIBRARYASSET Z_PRIMARYKEY
ZBKCOLLECTIONMEMBER Z_METADATA

问题

20250729

从epubcfi如epubcfi(/6/20[id155]!/4/412/1,:0,:97)去反推定位内容,没法实现,浪费时间。

解决思路

  1. 基本信息
  • 笔记数据在这个表中:~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite
  • 书籍清单在这个表中:~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite
  • 书籍元数据在这里:~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist
  • 书籍在这个directory中(epub为文件夹非压缩文件)~/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents
  1. copy以上前三个文件到./data目录下分别命名为AEAnnotation.sqlite、BKLibrary.sqlite、Books.plist BKLibrary.sqlite暂不使用使用Books.plist

  2. 解析Books.plist存在名为booksinfo的defaultdict(dict)中。booksinfo数据结构如下 booksinfo = { "768E1CD0B3086166F791683869B12425": { "BKDisplayName" : "最好的辩护-德肖维茨", "artistName" : "(美)德肖维茨", "BKBookType" : "epub", "BKGeneratedItemId" : "768E1CD0B3086166F791683869B12425", "itemName" : "最好的辩护", "path" : "/Users/gavin/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents/最好的辩护.epub" } …… } 其中768E1CD0B3086166F791683869B12425为BKGeneratedItemId。

  3. 查询AEAnnotation.sqlite获取ZANNOTATIONCREATIONDATE ZANNOTATIONASSETID ZANNOTATIONLOCATION ZANNOTATIONSELECTEDTEXT ZANNOTATIONUUID 如: ZANNOTATIONCREATIONDATE 746202137.8 #需要转化为2025/7/30这样的格式。 ZANNOTATIONASSETID 5F2C1C40566C61A2267907E621A664A4 ZANNOTATIONLOCATION epubcfi(/6/98[id61]!/4[1CQAE0-902f5dcbb4a04a858d573ec5ee66e862]/58/1,:28,:226) #需要获取idref = id61filepos=1CQAE0-902f5dcbb4a04a858d573ec5ee66e862 ZANNOTATIONSELECTEDTEXT 这才是对人而言的自由 ZANNOTATIONUUID 0456103A-7161-451A-B762-1305ECBECBDB 并把数据存到名为annotations的defaultdict(dict)中。annotations的数据结构如下 annotations = { 'CB9A605DCD687C4FA544DD4BCCD00D43': { '326CA2CF-3298-45D5-A93F-440E6F2A0B33': { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '這就是宣傳的恐怖之處'}, '2BD61C93-D1F1-4553-90CB-043A6E06DBA1': { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '應該有一些系統性的表達機制'}, } …… }

  4. 需要导出的书名作为参数,比如"最好的辩护"书名做从booksinfo中做模糊查询如匹配到多本在shell中提示选择哪一本书。 从booksinfo获取本书的uuid和这本书epub的路径path进而从annotations中获取这本书的笔记。

  5. 依据这本书的路径path解析epub文件夹下的文件。 假定epub目录为epubdirxx.opf(package.opf|standard.opf|content.opf)和toc.ncx文件可能的位置在 epubdir/content.opf epubdir/toc.ncx epubdir/OEBPS/content.opf epubdir/OEBPS/toc.ncx epubdir/EPUB/package.opf epubdir/EPUB/toc.ncx epubdir/item/standard.opf epubdir/item/toc.ncx

  • 解析opf文件存入default(dict)数据结构contentopf: contentopf = { "768E1CD0B3086166F791683869B12425": { id163 : index_split_000.html, # idref: ref id162 : index_split_001.html, id161 : index_split_002.html, id160 : index_split_003.html, id159 : index_split_004.html, id158 : index_split_005.html …… } }

  • 解析toc.ncx文件存入default(dict)数据结构contenttoc: contenttoc = { "768E1CD0B3086166F791683869B12425": { { 'num_1': {'filepos': None, 'label': '\n最好的辩护\n', 'ref': 'index_split_000.html'}, 'num_100': { 'filepos': None, 'label': '\n第八章父亲的罪\n', 'num_101': { 'filepos': 'filepos698889', 'label': '\n没有父亲的生活\n', 'ref': 'index_split_016.html'}, 'num_102': {'filepos': 'filepos706518', 'label': '\n越狱\n', 'ref': 'index_split_016.html'}, …… } } }

  1. 使用annotationdata.py获取annotations把annotations扩充label_path 通过annotations的assetid从bookinfo获取path路径在path下找到.opf和.ncx使用opf_parse.py和toc_parse.py获取label_path

booksnote = { assetid: { label_path: { uuid: { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '這就是宣傳的恐怖之處' }}}}

应该从epubcfi中的id155去找到html文件在从笔记内容去匹配对应文件中的位置。 需要有个函数吧html的数据结构dict化很快可以从内容找到title。

从epubcfi的ZANNOTATIONASSETID找到bookid 从ZANNOTATIONASSETID 依据bookid => epub路径 解析book到dict中。 解析content.opf中的[id,href] 关联idtitle章节内容 => bookdict = [id,title,[内容]] 从ZAEANNOTATION的笔记内容遍历bookdict找到内容找到title 最后按booknametitle归并笔记