EPUB文件HTML批量修改避坑

SigilCalibre通常可以用来对EPUB文件的内容进行修改,但其正则表达式修改能力都比较有限,对于过于复杂的修改有点无能为力。最近下载了一本中国文学鉴赏词典大系,显然是要求不高的人士制作的,其中将诗歌原文与鉴赏用了相同的样式类,举出其中一首如下:

html 复制代码
<h3 class="calibre11" id="bw550">父亲</h3>
<p class="fbt-left">------题罗中立同名油画</p>
<p class="shuming1">桂兴华</p>
<p class="tzshort">
<span class="bold">一</span>
</p>
<p class="normaltext">一道道牛车的辙印,</p>
<p class="normaltext">化成了你额上的皱纹。</p>
<p class="normaltext">那是你拖着小村的贫困,</p>
<p class="normaltext">绕过了弯弯曲曲的田埂------</p>
<p class="normaltext">既犁下了对早春的爱,</p>
<p class="normaltext">又犁下了对寒冬的恨......</p>
<p class="tzshort">
<span class="bold">二</span>
</p>
<p class="normaltext">这碗清亮亮的井水,</p>
<p class="normaltext">是你赤裸着古铜色的背脊,</p>
<p class="normaltext">一次,又一次,</p>
<p class="normaltext">从荒岗深深的岩层下吊起。</p>
<p class="normaltext">在这烫得快要冒烟的炎夏呵,</p>
<p class="normaltext">我多想,多想成为一口井,</p>
<p class="normaltext">盛满了你的梦,供你汲取。</p>
<p class="normaltext">让我清澈的心愿,</p>
<p class="normaltext">留在你干枯的瞳仁里......</p>
<p class="tzshort">
<span class="bold">三</span>
</p>
<p class="normaltext">你的沉默是凝固的话,</p>
<p class="normaltext">像这黄土一样板实。</p>
<p class="normaltext">沉默,在记录风的纷乱;</p>
<p class="normaltext">沉默,在活埋雨的腥热;</p>
<p class="normaltext">沉默呵,在迎接开垦的季节。</p>
<p class="normaltext">终于,春天扛着木犁来了,来了!</p>
<p class="normaltext">你坦开了郁积多年的相约:</p>
<p class="normaltext">撒一把种子,</p>
<p class="normaltext">就有一树红高粱的喜悦......</p>
<p class="tzshort">
<span class="bold">四</span>
</p>
<p class="normaltext">别再让烟</p>
<p class="normaltext">熏焦你枯枝般的指头。</p>
<p class="normaltext">最后吸一口吧,</p>
<p class="normaltext">最后缓缓地吸一口。</p>
<p class="normaltext">然后,把愁思抛丢------</p>
<p class="normaltext">看!霞一般的苹果园里,</p>
<p class="normaltext">不正点起红艳艳的秋?</p>
<p class="normaltext">这飘动缕缕香馨的秋呵,</p>
<p class="normaltext">将在你的栽培下燃个不休!......</p>
<p class="luokuan">选自《第一次诱惑》,学林出版社1987年版</p>
<p class="normaltext1">诗人在给画家的同名油画题诗。写得诗中见画,画中见情。在题画诗里不可多得。</p>
<p class="normaltext">诗中再现清晰生动的画面,传达出黄土高原特有的风情:土地的板实,"小村的贫困","弯弯曲曲的田埂","一道道的辙印","清凉凉的井水","深深的岩层"。在这充满地域特色的背景上活动着形象丰满、个性突出的人物------扶犁开垦的饱经沧桑的一位山区老农,他赤裸着古铜色的背脊,额上镌刻道道皱纹,瞳仁干枯,指头熏焦,在"烫得快要冒烟的炎夏"里,"坦开了郁积多年的相约",扶犁开垦,播撒希望。那碗"从荒岗深深的岩层下吊起"的"清凉凉的井水",映照着他甜美的梦。</p>

读起来原文与鉴赏在视觉上没有差别,让我觉得有点别扭。好在原文与鉴赏分隔的第一个段落样式类稍有不同,叫作normaltext1,因此,可以考虑将类名为shuming1normaltext1p标签之间的类名为normaltextp标签的类名改为original-text。但是这样复杂的修改(主要是文件数量太多),用SigilCalibre没法批量完成,优先考虑用Python来解决。

Python修改EPUB文件,首先想到的就是ebooklib,然而尽管这个库的文档声称item.getContent()方法能够返回完整内容,实际上却会丢失<head>标签中的内容,而且即使后面拼接itemcontent时加入了<head>标签,但最终epub.write_epub()的时候,还是会将<head>标签的内容丢掉,只留一个空的<head>标签占位。样式表什么的还好说,关键是title,每个文件都不同,所以也没法在事后用SigilCalibre替换回去,这可以说是ebooklib的巨坑。

好在EPUB文件本质上是压缩文件,所以可以用zipfile处理,在修改内容方面,可以用正则表达式或者BeautifulSoup,但是原生的正则表达式库不能支持不定长度的匹配,要安装并使用regex库。

下面的代码包含了用BeautifulSoupregex修改上述EPUB文件的方法,当然,实际操作中只能用一种方法,因此注释掉了使用BeautifulSoup的代码:

python 复制代码
import zipfile, regex
from bs4 import BeautifulSoup

def replace_normaltext_in_epub(input_path, output_path):
    # 使用 zipfile 直接读取和修改 EPUB 文件
    with zipfile.ZipFile(input_path, 'r') as zf:
        # 创建一个临时目录来存储解压后的文件
        import tempfile
        import os
        temp_dir = tempfile.mkdtemp()

        # 解压所有文件到临时目录
        zf.extractall(temp_dir)

        # 遍历所有 HTML 文件
        for file_name in zf.namelist():
            if file_name.endswith('.html') or file_name.endswith('.xhtml'):
                file_path = os.path.join(temp_dir, file_name)
                with open(file_path, 'r', encoding='utf-8') as f:
                    html_content = f.read()

                # 使用 BeautifulSoup 解析 HTML
                # soup = BeautifulSoup(html_content, 'html.parser')

                # # 处理 <body> 内容
                # body_tag = soup.body
                # if not body_tag:
                #     continue

                # # 找到所有 <p class="shuming1"> 标签
                # shuming1_tags = body_tag.find_all('p', class_='shuming')

                # for shuming1 in shuming1_tags:
                #     current = shuming1.find_next()
                #     while current:
                #         if 'normaltext1' in current.get('class', []) and current.name == 'p':
                #             break
                #         if 'normaltext' in current.get('class', []) and current.name == 'p':
                #             current['class'] = ['original-text']
                #         current = current.find_next()

                # 对 html_content 内容进行替换
                pattern = r'(?s)(<p class="shuming1">.*?</p>)(.*?)(?=<p class="normaltext1">)'
                def replacer(match):
                    shuming_part = match.group(1)
                    middle_part = match.group(2)
                    replaced_middle = regex.sub(r'<p class="normaltext">(.*?)</p>', r'<p class="original-text">\1</p>', middle_part)
                    return shuming_part + replaced_middle

                # 执行替换
                new_html_content = regex.sub(pattern, replacer, html_content)

                # 保存修改后的 HTML
                # new_html_content = str(soup)
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(new_html_content)

        # 重新打包为 EPUB 文件
        with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as new_zf:
            for root, dirs, files in os.walk(temp_dir):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, temp_dir)
                    new_zf.write(file_path, arcname)

        print(f'重新制作"{output_path}"完成')

        # 删除临时目录
        import shutil
        shutil.rmtree(temp_dir)

# 示例
replace_normaltext_in_epub(
    r'E:\下载\中国文学鉴赏辞典大系_new.epub',
    r'E:\下载\中国文学鉴赏辞典大系.epub')

执行后前面举的例子变成了:

html 复制代码
<h3 class="calibre11" id="bw550">父亲</h3>
<p class="fbt-left">------题罗中立同名油画</p>
<p class="shuming1">桂兴华</p>
<p class="tzshort">
<span class="bold">一</span>
</p>
<p class="original-text">一道道牛车的辙印,</p>
<p class="original-text">化成了你额上的皱纹。</p>
<p class="original-text">那是你拖着小村的贫困,</p>
<p class="original-text">绕过了弯弯曲曲的田埂------</p>
<p class="original-text">既犁下了对早春的爱,</p>
<p class="original-text">又犁下了对寒冬的恨......</p>
<p class="tzshort">
<span class="bold">二</span>
</p>
<p class="original-text">这碗清亮亮的井水,</p>
<p class="original-text">是你赤裸着古铜色的背脊,</p>
<p class="original-text">一次,又一次,</p>
<p class="original-text">从荒岗深深的岩层下吊起。</p>
<p class="original-text">在这烫得快要冒烟的炎夏呵,</p>
<p class="original-text">我多想,多想成为一口井,</p>
<p class="original-text">盛满了你的梦,供你汲取。</p>
<p class="original-text">让我清澈的心愿,</p>
<p class="original-text">留在你干枯的瞳仁里......</p>
<p class="tzshort">
<span class="bold">三</span>
</p>
<p class="original-text">你的沉默是凝固的话,</p>
<p class="original-text">像这黄土一样板实。</p>
<p class="original-text">沉默,在记录风的纷乱;</p>
<p class="original-text">沉默,在活埋雨的腥热;</p>
<p class="original-text">沉默呵,在迎接开垦的季节。</p>
<p class="original-text">终于,春天扛着木犁来了,来了!</p>
<p class="original-text">你坦开了郁积多年的相约:</p>
<p class="original-text">撒一把种子,</p>
<p class="original-text">就有一树红高粱的喜悦......</p>
<p class="tzshort">
<span class="bold">四</span>
</p>
<p class="original-text">别再让烟</p>
<p class="original-text">熏焦你枯枝般的指头。</p>
<p class="original-text">最后吸一口吧,</p>
<p class="original-text">最后缓缓地吸一口。</p>
<p class="original-text">然后,把愁思抛丢------</p>
<p class="original-text">看!霞一般的苹果园里,</p>
<p class="original-text">不正点起红艳艳的秋?</p>
<p class="original-text">这飘动缕缕香馨的秋呵,</p>
<p class="original-text">将在你的栽培下燃个不休!......</p>
<p class="luokuan">选自《第一次诱惑》,学林出版社1987年版</p>
<p class="normaltext1">诗人在给画家的同名油画题诗。写得诗中见画,画中见情。在题画诗里不可多得。</p>
<p class="normaltext">诗中再现清晰生动的画面,传达出黄土高原特有的风情:土地的板实,"小村的贫困","弯弯曲曲的田埂","一道道的辙印","清凉凉的井水","深深的岩层"。在这充满地域特色的背景上活动着形象丰满、个性突出的人物------扶犁开垦的饱经沧桑的一位山区老农,他赤裸着古铜色的背脊,额上镌刻道道皱纹,瞳仁干枯,指头熏焦,在"烫得快要冒烟的炎夏"里,"坦开了郁积多年的相约",扶犁开垦,播撒希望。那碗"从荒岗深深的岩层下吊起"的"清凉凉的井水",映照着他甜美的梦。</p>

这样,只需在EPUB文件的样式文件中增加.original-text样式定义,就可以将原文与鉴赏区分了。

相关推荐
B站_计算机毕业设计之家1 小时前
python手写数字识别系统 CNN算法 卷积神经网络 OpenCV和Keras模型 计算机视觉 (建议收藏)✅
python·深度学习·opencv·机器学习·计算机视觉·cnn
郝学胜-神的一滴1 小时前
Python高级编程技术深度解析与实战指南
开发语言·python·程序人生·个人开发
charlie1145141911 小时前
使用 Poetry + VS Code 创建你的第一个 Flask 工程
开发语言·笔记·后端·python·学习·flask·教程
Valueyou241 小时前
引入基于加权 IoU 的 WiseIoU 回归损失以提升 CT 图像检测鲁棒性
人工智能·python·深度学习·目标检测
熊猫钓鱼>_>2 小时前
多维度股票量化指标体系详解
python·股票·量化·指标·趋势·macd·估值
傻啦嘿哟2 小时前
Python将Excel工作表转换为PDF:从入门到实战
python·pdf·excel
老鱼说AI2 小时前
BPE编码从零开始实现pytorch
开发语言·人工智能·python·机器学习·chatgpt·nlp·gpt-3
陳陈陳2 小时前
AIGC 时代,用自然语言操作数据库:SQLite + LLM 的轻量级实践
前端·数据库·python
林炳然3 小时前
Python-Basic Day-4 函数-基础知识
python