Sigil和Calibre通常可以用来对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,因此,可以考虑将类名为shuming1与normaltext1的p标签之间的类名为normaltext的p标签的类名改为original-text。但是这样复杂的修改(主要是文件数量太多),用Sigil和Calibre没法批量完成,优先考虑用Python来解决。
用Python修改EPUB文件,首先想到的就是ebooklib,然而尽管这个库的文档声称item.getContent()方法能够返回完整内容,实际上却会丢失<head>标签中的内容,而且即使后面拼接item的content时加入了<head>标签,但最终epub.write_epub()的时候,还是会将<head>标签的内容丢掉,只留一个空的<head>标签占位。样式表什么的还好说,关键是title,每个文件都不同,所以也没法在事后用Sigil或Calibre替换回去,这可以说是ebooklib的巨坑。
好在EPUB文件本质上是压缩文件,所以可以用zipfile处理,在修改内容方面,可以用正则表达式或者BeautifulSoup,但是原生的正则表达式库不能支持不定长度的匹配,要安装并使用regex库。
下面的代码包含了用BeautifulSoup和regex修改上述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样式定义,就可以将原文与鉴赏区分了。