You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ebooklib从现有EPUB生成合规的最小化EPUB文件?

问题描述

我希望使用Python和ebooklib从现有EPUB文件生成一个「最小化」的EPUB版本,仅保留必要元数据(title、creator、date、publisher)、封面图片、目录(toc)、书脊(spine)及全部文本,移除多余的元数据、图片、字体等内容。

以下是我当前的代码:

import ebooklib as ebl
from ebooklib import epub

def minimal_epub(filename):
  old, new = epub.read_epub(filename), epub.EpubBook()

  for key in ['title', 'creator', 'date', 'publisher']:
    metadata = old.get_metadata('DC', key)
    if metadata: new.add_metadata('DC', key, metadata[0][0])
    print(new.get_metadata('DC', key)) # show current metadata field

  print(new.metadata) # show all metadata fields
  
  # assume that the largest image in archive is the cover image 
  sortkey = lambda item: -len(item.content) # sort images by decreasing sizes
  cover = sorted(old.get_items_of_type(ebl.ITEM_IMAGE), key=sortkey)[0]
  new.set_cover(cover.file_name, cover.content) # keep largest image as cover
  print(cover.file_name) # show cover filename    

  for item in old.get_items_of_type(ebl.ITEM_DOCUMENT):
    new.add_item(item); print(item) # show name of document items

  for item in old.get_items_of_type(ebl.ITEM_STYLE):
    new.add_item(item); print(item) # show name of styles

  new.toc = old.toc; print(new.toc) # show links in table of content
  new.spine = old.spine; print(new.spine) # show spine
  
  new.add_item(epub.EpubNcx()); new.add_item(epub.EpubNav()) # navigation

  epub.write_epub(filename.replace('.','+.'), new)

>>> minimal_epub('path/to/file.epub')

我无法直接定位EPUB文件中的封面图片,因此假设体积最大的图片为封面,该方法多数情况下可行。但生成的最小化EPUB文件仅约80%能被标准EPUB阅读器正常打开,剩余部分会因格式错误无法正确显示。推测可能遗漏了某些必要文件,但我对EPUB格式不熟悉,且ebooklib文档不够详尽,希望能得到相关建议。


解决方案

1. 精准识别封面图片

不要依赖文件大小判断封面,EPUB规范中封面通常会在元数据或OPF文件里标记,优先通过规范方式获取:

# 从元数据提取封面ID
cover_id = None
for meta in old.metadata.get('http://www.idpf.org/2007/opf', 'meta'):
    if meta[0].get('name') == 'cover':
        cover_id = meta[0]['content']
        break

# 根据ID获取封面图,找不到再用原方法兜底
if cover_id:
    cover_item = old.get_item_with_id(cover_id)
    if cover_item:
        new.set_cover(cover_item.file_name, cover_item.content)
else:
    sortkey = lambda item: -len(item.content)
    cover = sorted(old.get_items_of_type(ebl.ITEM_IMAGE), key=sortkey)[0]
    new.set_cover(cover.file_name, cover.content)

2. 修复目录与书脊的引用失效问题

直接赋值旧书的toc和spine会导致内部归属关系混乱,需要重新构建:

重构目录(TOC)

new.toc = []
for toc_item in old.toc:
    if isinstance(toc_item, epub.Link):
        linked_item = old.get_item_with_href(toc_item.href)
        if linked_item in new.items:
            new_link = epub.Link(linked_item.href, toc_item.title, toc_item.id)
            new.toc.append(new_link)
    elif isinstance(toc_item, tuple):
        # 处理嵌套目录
        new_subitems = []
        for sub_item in toc_item[1]:
            if isinstance(sub_item, epub.Link):
                linked_item = old.get_item_with_href(sub_item.href)
                if linked_item in new.items:
                    new_subitems.append(epub.Link(linked_item.href, sub_item.title, sub_item.id))
        new.toc.append( (toc_item[0], new_subitems) )

重构书脊(Spine)

new.spine = ['nav']  # 新版EPUB必须包含nav项
for item_id in old.spine:
    if item_id == 'ncx':
        continue  # 用nav替代ncx,无需保留
    item = old.get_item_with_id(item_id)
    if item and item in new.items:
        new.spine.append(item.id)

3. 正确处理导航文件

优先保留原书的导航文件,避免ebooklib自动生成的导航与原结构冲突:

# 检查原书是否有自定义nav项
nav_item = None
for item in old.get_items_of_type(ebl.ITEM_NAVIGATION):
    nav_item = item
    break
if nav_item:
    new.add_item(nav_item)
    if nav_item.id not in new.spine:
        new.spine.insert(0, nav_item.id)
else:
    new.add_item(epub.EpubNav())

4. 只保留被引用的样式文件

并非所有样式都必要,仅保留文档实际引用的样式:

# 收集所有文档引用的样式href
used_styles = set()
for doc_item in old.get_items_of_type(ebl.ITEM_DOCUMENT):
    content = doc_item.content.decode('utf-8')
    # 简单匹配<link>标签中的样式引用
    import re
    matches = re.findall(r'<link[^>]+href="([^"]+)"[^>]+stylesheet', content)
    for href in matches:
        used_styles.add(href)

# 仅添加被引用的样式
for item in old.get_items_of_type(ebl.ITEM_STYLE):
    if item.href in used_styles:
        new.add_item(item)

5. 验证生成的EPUB格式

生成文件后,用EPUB验证工具(如IDPF EPUB Validator)检查错误,常见问题包括:

  • 确保所有item的ID唯一无重复
  • 元数据中的日期符合ISO 8601格式(如2023-10-05)
  • 封面图片的MIME类型正确(ebooklib的set_cover通常会自动处理,但特殊格式需手动指定)

整合以上步骤后,生成的EPUB兼容性会大幅提升。

内容的提问来源于stack exchange,提问作者sciroccorics

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 08:20:21