Complete EPUB creation, editing, validation, and knowledge extraction skill for the Agent Skills open format. Built from spec research, real EPUB testing on 2.1MB commercial Apress title, and Apple Books compatibility verification on macOS 26. Scripts (11): epub-scaffold — Create valid EPUB3 with cover XHTML, Apple Books CSS epub-edit — Surgical editing (8 subcommands, epublib) epub-info — Structure/metadata dump as JSON epub-text — Clean text extraction, per-chapter or single-file epub-extract-knowledge — Heuristic + LLM extraction (env var auto-detect) epub-validate — EPUBCheck or Python fallback validation epub-images — List/extract all images with cover detection epub-batch — Multi-file processing (extract-text, validate, metadata) epub-convert — EPUB2→EPUB3 conversion with validation epub-repair — Diagnose & auto-fix common structural issues epub-cover — Add cover XHTML wrapper for Apple Books compatibility References (9): epub-format-internals.md, python-libraries.md, spec-and-validation.md, tutorials-and-guides.md, agent-capability-discovery.md, fixed-layout-epub.md, accessibility.md, media-overlays.md, apple-books-compatibility.md (NEW — verified on macOS 26) Test: 46/46 passing (test_epub_skill.sh)
11 KiB
Python Libraries for EPUB
Three main Python libraries exist for EPUB work. This reference covers their capabilities, tradeoffs, and when to use each.
Comparison
| Library | License | EPUB | Status | PyPI | Best For |
|---|---|---|---|---|---|
| EbookLib | AGPL | 2/3 | Active (v0.20) | ebooklib |
Creating & reading from scratch |
| epublib | MIT | 3 only | Active (2026) | epublib |
Editing existing EPUBs |
| pyepub | MIT | 2 only | Discontinued (2020) → yael |
pyepub |
Do not use for new work |
EbookLib
The most widely used Python EPUB library. Handles reading, writing, and basic manipulation of EPUB2 and EPUB3 files.
Installation
pip install EbookLib
Reading
import ebooklib
from ebooklib import epub
book = epub.read_epub('book.epub')
# Get all items of a specific type
for item in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
print(item.get_name(), item.get_content())
# Get images
for image in book.get_items_of_type(ebooklib.ITEM_IMAGE):
with open(image.get_name(), 'wb') as f:
f.write(image.get_content())
# Access metadata
title = book.get_metadata('DC', 'title')
creator = book.get_metadata('DC', 'creator')
Creating
from ebooklib import epub
book = epub.EpubBook()
book.set_identifier('urn:uuid:123e4567-e89b-12d3-a456-426614174000')
book.set_title('My Book')
book.set_language('en')
book.add_author('Author Name')
# Create a chapter
c1 = epub.EpubHtml(title='Chapter 1', file_name='chap1.xhtml', lang='en')
c1.content = '<h1>Chapter 1</h1><p>Content here.</p>'
book.add_item(c1)
# Add an image
img = epub.EpubImage(
uid='cover',
file_name='images/cover.jpg',
media_type='image/jpeg',
content=open('cover.jpg', 'rb').read()
)
book.add_item(img)
# Define spine (reading order)
book.spine = ['nav', c1]
# Add navigation (EPUB3) and NCX (EPUB2 compat)
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())
# Write
epub.write_epub('out.epub', book)
Key Classes
| Class | Purpose |
|---|---|
EpubBook |
The book container |
EpubHtml |
XHTML content document (chapter) |
EpubImage |
Image resource |
EpubItem |
Generic resource (CSS, fonts, etc.) |
EpubNcx |
EPUB2 NCX navigation |
EpubNav |
EPUB3 NAV navigation |
EpubCover |
Cover page |
Link / Section |
TOC structure elements |
Strengths
- Most widely used — large community, many projects depend on it
- Supports both EPUB2 and EPUB3
- Simple, intuitive API for creating EPUBs from scratch
- Handles cover pages, TOC, spine, and metadata well
Weaknesses
- AGPL license — if you distribute software using EbookLib, your software must also be AGPL. Calling it at runtime (without bundling) is generally considered fine.
- Reading large EPUBs loads everything into memory
- Currently undergoing a refresh (see GitHub issue #318)
Documentation
- PyPI: https://pypi.org/project/EbookLib/
- ReadTheDocs: http://ebooklib.readthedocs.io
- GitHub: https://github.com/aerkalov/ebooklib
epublib
A newer library (2026) designed specifically for editing existing EPUB3 files. Memory-efficient, spec-compliant, and MIT-licensed.
Installation
pip install epublib
Key Design
- Memory-efficient: Lazy-loads resources from the ZIP as needed using
Python's
zipfilemodule. Does not load everything into memory at once. - Non-intrusive editing: When you open and save an EPUB, it preserves the original structure. Only modified parts are changed — the manifest isn't regenerated from scratch, metadata items keep their order, etc.
- Spec-compliant: Follows the EPUB 3.3 specification. Resource classes mirror the spec's type hierarchy.
- BeautifulSoup-based: Content documents (
ContentDocument) expose a.soupattribute for DOM manipulation.
Editing Example
from epublib import EPUB
with EPUB('book.epub') as book:
# Edit metadata
book.metadata.title = 'New Title'
# Edit all content documents
for doc in book.documents:
# Insert a heading at the top
new_h1 = doc.soup.new_tag('h1', string='New Heading')
doc.soup.body.insert(0, new_h1)
# Add a CSS file link to all documents
for doc in book.documents:
new_link = doc.soup.new_tag('link', rel='stylesheet',
href='../Styles/new.css',
type='text/css')
doc.soup.head.append(new_link)
book.update_manifest_properties()
book.write('book-modified.epub')
Resource Hierarchy
Resource
├── XMLResource (has .soup)
│ ├── PackageDocument (content.opf)
│ └── ContentDocument (XHTML, SVG)
│ └── NavigationDocument (nav.xhtml)
├── PublicationResource (has .media_type)
│ ├── ContentDocument (also above)
│ └── NCXFile (toc.ncx)
Resource Operations
# Add a resource
from epublib.resources.create import create_resource
new_resource = create_resource(xhtml_bytes, 'Text/chapter2.xhtml')
book.resources.add(resource=new_resource, add_to_spine=True, after='Text/chapter1.xhtml')
# Remove a resource
book.resources.remove('Text/chapter1.xhtml')
# Rename (auto-updates all references)
book.resources.rename('Text/chapter1.xhtml', 'Text/chapter-one.xhtml')
# Filter by media type
from epublib.media_type import MediaType, Category
pngs = book.resources.filter(MediaType.IMAGE_PNG)
images = book.resources.filter(Category.IMAGE) # all image types
Strengths
- MIT license — no AGPL concerns
- Memory-efficient for large EPUBs
- Spec-compliant resource type hierarchy
- Non-intrusive editing preserves original structure
- BeautifulSoup integration for content manipulation
- Resource renaming auto-updates all references
Weaknesses
- Newer library — smaller community, fewer tutorials
- EPUB3 only (no EPUB2 creation, though EPUB2 files can be read)
- Creating EPUBs from scratch requires more manual setup than EbookLib
- Can't write to the same file (must write to temp file then copy — EOFError)
Real-World API Quirks (from testing on 2.1MB commercial EPUB)
These were discovered during integration testing and are not in the docs:
book.resourcesis iterable, not dict-like. There is no.all()method. Usefor r in book.resources:directly.len(book.resources)works.metadata.authordoesn't exist. Dublin Core usesdc:creator. Read it viabook.metadata.items— iterate and checkitem.name == 'creator'.- Write to temp file, never same file.
EPUB(src)opens the ZIP, andbook.write(src)will fail withEOFErrorbecause the source file is truncated while epublib still reads from it. Write to a temp file then copy:book.write(tmp); shutil.copy(tmp, src). - Python 3.13+ required. The wheel targets
>=3.13. Install withpython3 -m pip install epublib— the barepipcommand may point to an older Python. book.documents[0]may be SVG, not XHTML. The first content document isn't guaranteed to be the first chapter. Filter by filename or checkmedia_type.update_manifest_properties()must be called explicitly after adding or modifying resources. epublib doesn't auto-recalculate manifest properties.remove_item(item)takes the item object, not a string. Usebook.metadata.remove_item(item)whereitemis a metadata item object. Themetadata.itemsattribute is atuple, so.items.remove()fails.- First document after cover may be nav. When the cover XHTML is in the spine,
book.documentsordering reflects spine order, not alphabetical. book.write()with cover image >2MB is slow. The entire zip is rewritten; epublib doesn't do incremental ZIP updates. For large covers, expect 2-5 second write times.
Documentation
Common Editing Patterns (epublib)
These are the patterns used by the epub-edit CLI. When writing custom editing
logic, follow these recipes:
Update metadata:
with EPUB('book.epub') as book:
book.metadata.title = 'New Title'
book.metadata.author = 'Author Name'
book.metadata.language = 'fr'
book.write('book-updated.epub')
Add a chapter from an XHTML file:
from epublib.resources.create import create_resource_from_path
with EPUB('book.epub') as book:
new = create_resource_from_path('new-chapter.xhtml', 'Text/chapter3.xhtml')
book.resources.add(resource=new, add_to_spine=True, add_to_toc=True,
after='Text/chapter2.xhtml')
book.update_manifest_properties()
book.write('book-expanded.epub')
Remove a chapter:
with EPUB('book.epub') as book:
book.resources.remove('Text/chapter2.xhtml')
book.write('book-trimmed.epub')
Reorder spine:
with EPUB('book.epub') as book:
book.spine.reorder(['chapter3', 'chapter1', 'chapter2', 'nav'])
book.write('book-reordered.epub')
Inject CSS into all documents:
from epublib.resources.create import create_resource_from_path
with EPUB('book.epub') as book:
css = create_resource_from_path('dark.css', 'Styles/dark.css')
book.resources.add(resource=css, add_to_spine=False)
for doc in book.documents:
link = doc.soup.new_tag('link', rel='stylesheet',
href='../Styles/dark.css', type='text/css')
doc.soup.head.append(link)
book.write('book-dark.epub')
Rename a resource (auto-updates all references):
with EPUB('book.epub') as book:
book.resources.rename('Images/old.jpg', 'Images/new.jpg')
book.write('book-renamed.epub')
When to Use Which
| Task | Use |
|---|---|
| Create a new EPUB from scratch | EbookLib |
| Read EPUB for text extraction | Either (EbookLib simpler, epublib more memory-efficient) |
| Edit existing EPUB (modify metadata, reorder spine, add/remove chapters) | epublib |
| Modify content within chapters | epublib (BeautifulSoup access) |
| Convert EPUB2 → EPUB3 | epublib (non-intrusive editing) |
| Batch process many EPUBs | epublib (memory-efficient) |
| Quick one-off script, don't care about license | EbookLib |
Running Without Installation
The scripts in this skill require at least EbookLib. To install dependencies:
pip install EbookLib
# Optional: for advanced editing
pip install epublib
License Note
EbookLib is AGPL. The scripts in this skill call it at runtime but do not bundle or distribute it. Users install it independently via pip. This is the standard pattern for MIT-licensed tools that wrap AGPL libraries.