Parsing and Rebuilding EPUB Files in Python: Lessons Learned from LectuLibre LectuLibre, a service that translates books using AI, has shared the technical challenges and solutions for parsing and rebuilding EPUB files in Python. The team evaluated several libraries and settled on EbookLib for reading, lxml for HTML parsing, and Python's zipfile for rebuilding, while addressing issues like character encoding and broken manifests. They also developed a method to rebuild EPUBs directly as ZIP files to maintain fine-grained control. How we tackled EPUB parsing and rebuilding for AI book translation, with code examples and hard-won lessons. At LectuLibre https://lectulibre.com https://lectulibre.com , we translate books using AI. Users upload EPUB or PDF files, and our system translates the text inside while preserving the original formatting. Sounds straightforward, right? Well, we quickly learned that handling EPUB files in production is anything but trivial. In this article, I'll share the real-world challenges we faced parsing and rebuilding EPUBs with Python, the libraries we chose, the code we wrote, and the lessons we learned along the way. An EPUB is essentially a ZIP archive containing HTML, CSS, images, and a manifest file content.opf . To translate a book, we needed to: Simple in theory, but real-world EPUBs are messy. Some have broken manifests, missing required files mimetype , META-INF/container.xml , or text encoded in obscure charsets. Others are huge, with hundreds of images that we don't want to load into memory. Our translation pipeline needed to be fast, memory-efficient, and resilient to broken input. We evaluated several options: We settled on EbookLib for most reading tasks, lxml for high-performance HTML parsing, and Python's built-in zipfile for the final rebuilding step where we needed fine-grained control. This combination gave us the right balance of development speed and runtime performance. First, we read the EPUB using EbookLib and iterate over the spine the linear reading order . We only process items of type ITEM DOCUMENT HTML files . python import ebooklib from ebooklib import epub book = epub.read epub 'path/to/book.epub' translatable segments = for item in book.get items of type ebooklib.ITEM DOCUMENT : content = item.get body content .decode 'utf-8' assuming utf-8 But wait – not all EPUBs use utf-8 Lesson 1: Encoding Hell Many EPUBs especially older ones encode their HTML in Windows-1252 or ISO-8859-1. EbookLib's get body content often returns bytes, and decoding as utf-8 raises UnicodeDecodeError. We added charset detection using chardet : python import chardet raw bytes = item.get content encoding = chardet.detect raw bytes 'encoding' or 'utf-8' text = raw bytes.decode encoding, errors='replace' Once decoded, we parse the HTML with lxml to extract plain text while preserving the structure for later reassembly. python from lxml import etree parser = etree.HTMLParser tree = etree.fromstring text.encode 'utf-8' , parser=parser Extract text with XPath, but keep track of element boundaries for elem in tree.iter : if elem.text: translatable segments.append { 'id': item.get id , 'tag': elem.tag, 'text': elem.text.strip , storing path for later replacement 'path': tree.getpath elem } For LLM translation, we concatenate segments into manageable chunks, preserving paragraph boundaries to give the model context. After translation, we need to replace the original text in the HTML documents and reassemble the EPUB. EbookLib provides epub.write epub but we found it had a few quirks: We opted to manipulate the EPUB as a ZIP file directly for the rebuild step, while still using EbookLib's data structures to understand the original manifest. python import zipfile import shutil import os def rebuild epub original path, output path, translated segments : Read original EPUB as ZIP to get all files with zipfile.ZipFile original path, 'r' as zin: with zipfile.ZipFile output path, 'w', zipfile.ZIP DEFLATED as zout: for item in zin.infolist : if item.filename.endswith '.html' or item.filename.endswith '.xhtml' : html content = zin.read item.filename .decode 'utf-8' Replace translated segments using lxml tree = etree.fromstring html content.encode 'utf-8' , parser=parser for seg in translated segments: if seg 'id' == item to id item.filename : elem = tree.xpath seg 'path' 0 if seg 'path' else None if elem is not None and elem.text: elem.text = seg 'translated text' new content = etree.tostring tree, encoding='unicode' zout.writestr item, new content.encode 'utf-8' else: Copy other files images, css, etc. as-is zout.writestr item, zin.read item.filename Lesson 2: Preserve the Mimetype File The EPUB specification requires that the first file in the ZIP be the mimetype file, stored without compression. EbookLib does this automatically when writing, but when using zipfile manually, you must add it first with stored compression. We forgot this initially and our books were rejected by strict readers. Fix: zout.writestr 'mimetype', 'application/epub+zip', zipfile.ZIP STORED Users upload terribly broken EPUBs. We learned to validate early: python def validate epub file path : with zipfile.ZipFile file path, 'r' as zf: if 'META-INF/container.xml' not in zf.namelist : raise ValueError "Missing container.xml" if 'mimetype' not in zf.namelist : raise ValueError "Missing mimetype file" Also check content.opf exists and parses correctly We also check that the OPF file references all necessary resources. This catches many upload errors before they hit the expensive translation step. lxml and zipfile . chardet or a similar library to decode text safely. write epub might suffice. For complex rebuilds, consider manual ZIP manipulation. mimetype file correctly.At LectuLibre, this EPUB processing pipeline now handles hundreds of translations per week, allowing readers around the world to enjoy books in their native language. The journey was bumpy, but the lessons we learned have made our service robust and our engineering team wiser. What libraries or techniques have you used for EPUB processing in Python? We'd love to hear about alternatives to EbookLib or other war stories in the comments