利用ebooklib制作符合epub3规范的epub文件

[复制链接]
发表于 2025-11-16 04:05:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
扫描的pdf文档在电子装备上阅读时受到了很多限定,在OCR质量越来越高的本日,完全可以思量将一本扫描的pdf电子书改造成epub电子书。改造过程大抵如下:
1、OCR
可以用AI大概一些大厂的免费API接口举行辨认,可参阅本身写个OCR工具,某捷休想再从我这里收费_怎样自建ocr辨认库-CSDN博客
2、校稿
一样平常扫描质量较高的文档OCR的正确率极高,但会有一些常见错误,比方用VBA将word文档处理惩罚成支持弹出式表明的epub文档可用的html内容-CSDN博客一文中的例子中的带圈数字表明引用和表明编号常常会有罅漏和错误的,引号不能配对等。我们可以将辨认出的文本复制到Word中,表明引用和表明编号罅漏的地方可以同等用①补上(可以思量用Autohotkey做个键盘映射,便于输入①),然后在后续处理惩罚过程中将其调解正确。可以用下面的宏辅助查抄分段选择范围内的引号配对环境:
  1. Sub 检查引号配对()
  2.     Dim aPara As Paragraph, b As Boolean
  3.     Dim regLQuote, regRQuote As RegExp, mLQuote, mRQuote As IMatchCollection2
  4.     Set regLQuote = CreateObject("VBScript.RegExp")
  5.     Set regRQuote = CreateObject("VBScript.RegExp")
  6.     With regLQuote
  7.         .Global = True
  8.         .Pattern = "“"
  9.     End With
  10.     With regRQuote
  11.         .Global = True
  12.         .Pattern = "”"
  13.     End With
  14.    
  15.     For Each aPara In Selection.Range.Paragraphs
  16.         Set mLQuote = regLQuote.Execute(aPara.Range.Text)
  17.         Set mRQuote = regRQuote.Execute(aPara.Range.Text)
  18.         If mLQuote.Count <> mRQuote.Count Then
  19.             b = True
  20.             aPara.Range.Select
  21.             Exit For
  22.         End If
  23.     Next
  24.     If b Then
  25.         MsgBox "请检查选定段落中的引号配对错误"
  26.     Else
  27.         MsgBox "本次选择范围内未发现引号配对错误"
  28.     End If
  29. End Sub
复制代码
3、编辑文档的大纲布局
与直接编辑HTML相比,在Word中编辑文档的大纲布局非常方便。以处理惩罚《何逊集校注》为例,我们在Word中做如下图所示的处理惩罚(图中误将诗内容样式为“列表段落”输入为“标题段落”):

4、在每个标题3前面插入分节符,便于进一步遍历和处理惩罚文档。宏代码如下:
  1. Sub 在符合条件的段落前插入分节符()
  2.     Dim pos As Long, styleName$
  3.     styleName = "标题 3"
  4.    
  5.     With Selection
  6.         .HomeKey wdStory '光标回到文档开头,此时Selection.Start为0
  7.         Do
  8.             pos = .Start '先记录光标位置
  9.             .GoTo wdGoToHeading, wdGoToNext, 1 '向后移动到下一个标题,以标题为对象遍历文档
  10.             If .Start = pos Then Exit Do ' 光标位置不变则已遍历完所有标题,退出循环
  11.             
  12.             If .Paragraphs(1).Style = styleName Then
  13.                 .InsertBreak Type:=wdSectionBreakContinuous ' 连续型分节符
  14.             End If
  15.             
  16.         Loop
  17.     End With
  18. End Sub
复制代码
5、利用文档的特点,将Word文件的文本内容改为HTML。
以如上的何逊集校注为例,可以先用下面的VBA代码插入符合epub3规范的弹出式表明干系HTML标签:
  1. Sub 何逊集改为html字串()
  2.    
  3.     Dim regEx As RegExp, aSec As Section
  4.     Dim matches As IMatchCollection2
  5.     Dim aPara, prePara As Paragraph, b As Boolean, c%, i%
  6.     Dim paraTxtRng, tmpRng As Range, chapter%, href$
  7.    
  8.     Set regEx = CreateObject("VBScript.RegExp")
  9.     regEx.Global = True
  10.    
  11.     Set prePara = ActiveDocument.Paragraphs(1)
  12.     For Each aSec In ActiveDocument.Sections
  13.         i = 0 ' 校注内容的注释编号,每节恢复为0
  14.         chapter = chapter + 1
  15.         
  16.         For Each aPara In aSec.Range.Paragraphs
  17.             ' 象《七召》這種將全文分成多個部分的,每分一部分将章节号增加1
  18.             ' 如果最终epub文件每首诗做成一个html文件,其实就无需考虑章节问题
  19.             If prePara.Style = "正文" And aPara.Style = "列表段落" Then
  20.                 i = 0
  21.                 chapter = chapter + 1
  22.             End If
  23.             href = "c" & Format(chapter, "000") & "_"
  24.             If aPara.Style = "列表段落" Then ' 处理诗内容中的注释引用
  25.                 regEx.Pattern = "[\u2460-\u2473]" ' 带圈数字序号1~20
  26.                 Set tmpRng = aPara.Range
  27.                 Set matches = regEx.Execute(aPara.Range.Text)
  28.                 For c = 0 To matches.count - 1
  29.                     With tmpRng.Find
  30.                         .Text = matches.Item(c)
  31.                         .Wrap = wdFindContinue
  32.                         .Execute
  33.                     End With
  34.                     ' 由于使用了循环变量作为输入的序号,所以校稿时无需考虑注释引用编号的正确性
  35.                     tmpRng.Text = "<a id=""ref_" & href & Trim(Str(c + 1)) & """ epub:type=""noteref"" href=""#" & _
  36.                                 href & Trim(Str(c + 1)) & """><sup>" & "[" & Format(c + 1, "00") & "]" & "</sup></a>"
  37.                 Next c
  38.             ElseIf aPara.Style = "正文" Then
  39.                 regEx.Pattern = "^[\u2460-\u2473]"
  40.                 ' 检查正文段落开头是否是带圈数字序号,不是就不处理
  41.                 If regEx.test(aPara.Range.Text) Then
  42.                     i = i + 1
  43.                     ' 注意修改段落内容时为避免副作用,应该将分段符排除在修改范围之外
  44.                     Set paraTxtRng = ActiveDocument.Range(aPara.Range.Start, aPara.Range.End - 1)
  45.                     ' 这里的HTML标签内容主要考虑了符合epub3规范弹出式注释的编写要求
  46.                     paraTxtRng.Text = "<aside epub:type=""footnote"" id=""" & href & Trim(Str(i)) & _
  47.                         """ class=""comment""><a href=""#ref_" & href & Trim(Str(i)) & """>" & _
  48.                         "[" & Format(i, "00") & "]</a>" & Mid(aPara.Range.Text, 2, Len(aPara.Range.Text) - 2) & "</aside>"
  49.                 End If
  50.             End If
  51.             If Len(aPara.Range.Text) > 1 Then Set prePara = aPara
  52.         Next aPara
  53.     Next aSec
  54.    
  55. End Sub
复制代码
再根据各段落的性子将各段落用干系HTML标签包裹:
  1. Sub 为段落包裹html标签()
  2.     Dim aPara As Paragraph, paraTxtRng As Range
  3.     For Each aPara In ActiveDocument.Paragraphs
  4.         If Len(aPara.Range.Text) > 1 And Left(aPara.Range.Text, 1) <> "<" Then
  5.             Set paraTxtRng = ActiveDocument.Range(aPara.Range.Start, aPara.Range.End - 1)
  6.             If aPara.Style = "标题 2" Then
  7.                 paraTxtRng.Text = "<h2>" & paraTxtRng.Text & "</h2>"
  8.             ElseIf aPara.Style = "标题 3" Then
  9.                 paraTxtRng.Text = "<h3>" & paraTxtRng.Text & "</h3>"
  10.             ElseIf aPara.Style = "标题 4" Then
  11.                 paraTxtRng.Text = "<h4>" & paraTxtRng.Text & "</h4>"
  12.             ElseIf aPara.Style = "列表段落" Then
  13.                 paraTxtRng.Text = "<p class=""content"">" & paraTxtRng.Text & "</p>"
  14.             ElseIf aPara.Style = "正文" Then
  15.                 paraTxtRng.Text = "<p class=""normal"">" & paraTxtRng.Text & "</p>"
  16.             End If
  17.         End If
  18.     Next
  19. End Sub
复制代码
6、将Word中处理惩罚好的内容复制粘贴到任何支持正则表达式的文本处理惩罚软件中,比方Emeditor,通过将“<h3>”更换为“</section>\n<section><h3>”,然后删除第一个“</section>”,在末了补上一个“</section>”,将每首诗连带其题解和校注内容全部包裹到一对“<section>”和“</section>”之中。将原来在Word文档中添加的“<br>”标签也更换为“</p><p class="content">”,使其到达真正的分段效果。处理惩罚完成后生存此文本文件,比方定名“何逊集校注.htm”。
7、利用下面的Python代码,将上一步处理惩罚的文本文件分解成每首诗一个符合epub3规范弹出式表明要求的HTML文件:
  1. import os
  2. from bs4 import BeautifulSoup
  3. # HTML模板
  4. html_template = """
  5. <html xml:lang="zh-CN" xmlns="http://www.w3.org/1999/xhtml"  xmlns:epub="http://www.idpf.org/2007/ops">
  6. <head>
  7.     <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
  8.     <title>Title</title>
  9.     <link href="styles.css" rel="stylesheet" />
  10. </head>
  11. <body>
  12. </body>
  13. </html>
  14. """
  15. path = 'htmldemo'
  16. orig_file = '何逊集校注.htm'
  17. with open(os.path.join(path, orig_file), 'r', encoding='utf-8') as f:
  18.     content = f.read()
  19.     soup_content = BeautifulSoup(content, 'html.parser')
  20.     i = 0
  21.     # 将内容添加到HTML模板的<body>中
  22.     for element in soup_content.find_all('section'):
  23.         # 创建BeautifulSoup对象
  24.         soup_template = BeautifulSoup(html_template, 'html.parser')
  25.         h3 = element.find('h3')
  26.         title = h3.get_text()
  27.         soup_template.title.string = title
  28.         soup_template.body.append(element)
  29.         # 在文件名前加上序号做前缀,便于排序
  30.         i = i + 1
  31.         with open(os.path.join(path, 'output', f'c{i:0>3}_{title}.html'), 'w', encoding='utf-8') as html:
  32.             html.write(soup_template.prettify())
复制代码
8、然后可以用sigil等epub文件创建工具利用分割的HTML文件制作epub了。但是对于步伐员来说,不如利用ebooklib库编个Python步伐来处理惩罚:
  1. import os
  2. from ebooklib import epub
  3. def make_epub(input_folder, output_file):
  4.     # 创建一个Epub书籍对象
  5.     book = epub.EpubBook()
  6.     # 添加书名、作者等信息
  7.     book.set_identifier(output_file.split('.')[0])  # 使用输出文件名作为标识符
  8.     book.set_title('何逊集校注')
  9.     book.set_language('zh-CN')
  10.     # 设置作者
  11.     book.add_author("一只虎")
  12.     # 添加一个导航项
  13.     book.add_item(epub.EpubNcx())
  14.     nav = book.add_item(epub.EpubNav())
  15.     # 用于存储CSS和JS文件的路径
  16.     css_files = []
  17.     js_files = []
  18.     # 准备css文件和js文件
  19.     for root, dirs, files in os.walk(input_folder):
  20.         for file in files:
  21.             if file.endswith(".css"):
  22.                 file_path = os.path.join(root, file)
  23.                 with open(file_path, 'r', encoding='utf-8') as f:
  24.                     content = f.read()
  25.                     css = epub.EpubItem(uid=file, file_name=file, content=content, media_type='text/css')
  26.                     css_files.append(css)
  27.             elif file.endswith(".js"):
  28.                 file_path = os.path.join(root, file)
  29.                 with open(file_path, 'r', encoding='utf-8') as f:
  30.                     content = f.read()
  31.                     js = epub.EpubItem(uid=file, file_name=file, content=content,
  32.                                        media_type='application/javascript')
  33.                     js_files.append(js)
  34.     # 存储文本文件
  35.     chapter_files = []
  36.     # 添加目录文件
  37.     chapter_files.append(nav)
  38.     # 遍历文件夹中的所有文件
  39.     for root, dirs, files in os.walk(input_folder):
  40.         for file in files:
  41.             if file.endswith(".html"):
  42.                 file_path = os.path.join(root, file)
  43.                 chapter = epub.EpubHtml(title=file, file_name=file)
  44.                 with open(file_path, 'r', encoding='utf-8') as f:
  45.                     chapter.content = f.read()
  46.                 # 添加CSS和JS文件到书籍中
  47.                 for css in css_files:
  48.                     chapter.add_item(css)
  49.                 for js in js_files:
  50.                     chapter.add_item(js)
  51.                 # 文本文件加入章节
  52.                 book.add_item(chapter)
  53.                 chapter_files.append(chapter)
  54.                 # 添加目录项,可以将链接文本进行处理(这里删除了文件名前缀及文件扩展名)
  55.                 book.toc.append((epub.Link(file, file[5:-5]), file))
  56.     # 设置书籍的spine(书脊),对于epub3.0规范来说不设置的话制作出的书籍无法阅读
  57.     book.spine = chapter_files
  58.     # 添加CSS和JS文件到书籍中
  59.     for css in css_files:
  60.         book.add_item(css)
  61.     for js in js_files:
  62.         book.add_item(js)
  63.     # 假设封面图片在与html文件同一文件夹下,名为cover.jpg
  64.     cover_image_path = os.path.join(input_folder, "cover.jpg")
  65.     if os.path.exists(cover_image_path):
  66.         with open(cover_image_path, 'rb') as f:
  67.             cover_image = epub.EpubImage(uid="cover", file_name="cover.jpg", media_type="image/jpeg", content=f.read())
  68.         book.set_cover("cover.jpg", cover_image.content)
  69.         book.add_item(cover_image)
  70.     # 保存书籍为EPUB文件
  71.     epub.write_epub(output_file, book, {})
  72. if __name__ == '__main__':
  73.     # 输入文件夹和输出文件名
  74.     input_folder = 'htmldemo/output'
  75.     output_file = 'htmldemo/何逊集校注.epub'
  76.     # 调用函数生成EPUB文件
  77.     make_epub(input_folder, output_file)
复制代码
上面的代码包罗了插入css、js和封面图片的内容,但是没有干系内容也不影响上面的步伐乐成实验。利用ebooklib要注意的是:
1)插入的css和js不但要添加到册本中,还要添加到HTML文件中,否则样式表和js文件不会起作用。固然,添加到HTML文件中时ebooklib只是在相应的文件中插入一些<link>及<script>标签来引用干系文件。
2)必须让册本的“spine”指向文本文件的列表,否则epub文件无法阅读,添加的目次文件也应该参加文本文件列表,否则目次文件无法阅读。
上面的用ebooklib天生epub文件的代码中,用下面的代码添加目次和导航元素是官方文档上的写法:
  1. book.add_item(epub.EpubNcx())
  2. book.add_item(epub.EpubNav())
复制代码
但是这种写法偶尔间会爆出下面的错误:
  1. File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\epub.py", line 1746, in write_epub
  2.     epub.write()
  3.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\epub.py", line 1369, in write
  4.     self._write_items()
  5.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\epub.py", line 1356, in _write_items
  6.     self.out.writestr('%s/%s' % (self.book.FOLDER_NAME, item.file_name), self._get_nav(item))
  7.                                                                          ^^^^^^^^^^^^^^^^^^^
  8.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\epub.py", line 1212, in _get_nav
  9.     inserted_pages = get_pages_for_items([item for item in self.book.get_items_of_type(ebooklib.ITEM_DOCUMENT) \
  10.                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  11.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\utils.py", line 119, in get_pages_for_items
  12.     pages_from_docs = [get_pages(item) for item in items]
  13.                        ^^^^^^^^^^^^^^^
  14.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\utils.py", line 96, in get_pages
  15.     body = parse_html_string(item.get_body_content())
  16.            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  17.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\ebooklib\utils.py", line 48, in parse_html_string
  18.     html_tree = html.document_fromstring(s, parser=utf8_parser)
  19.                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  20.   File "E:\projects\OpenCVStudy\.venv\Lib\site-packages\lxml\html\__init__.py", line 738, in document_fromstring
  21.     raise etree.ParserError(
  22. lxml.etree.ParserError: Document is empty
复制代码
这应该是由于自动天生目次失败导致的“Document is empty”错误,只要表明掉
  1. book.add_item(epub.EpubNav())
复制代码
即可乐整天生文件,但是如许天生的文件没有导航元素,有些手机或平板电子书阅读App会找不到目次。将上面的代码改成下面如许,手动天生nav.xhtml的内容并添加进册本,就既可以克制出现错误,又为册本添加导航目次:
  1. import os
  2. from ebooklib import epub
  3. def make_epub(input_folder, output_file):
  4.     # 创建一个Epub书籍对象
  5.     book = epub.EpubBook()
  6.     # 添加书名、作者等信息
  7.     book.set_identifier(output_file.split('.')[0])  # 使用输出文件名作为标识符
  8.     book.set_title('何逊集校注')
  9.     book.set_language('zh-CN')
  10.     # 设置作者
  11.     book.add_author("一只虎")
  12.     # 用于存储CSS和JS文件的路径
  13.     css_files = []
  14.     js_files = []
  15.     # 准备css文件和js文件
  16.     for root, dirs, files in os.walk(input_folder):
  17.         for file in files:
  18.             if file.endswith('.css'):
  19.                 file_path = os.path.join(root, file)
  20.                 with open(file_path, 'r', encoding='utf-8') as f:
  21.                     content = f.read()
  22.                     css = epub.EpubItem(uid=file, file_name=file, content=content, media_type='text/css')
  23.                     css_files.append(css)
  24.             elif file.endswith('.js'):
  25.                 file_path = os.path.join(root, file)
  26.                 with open(file_path, 'r', encoding='utf-8') as f:
  27.                     content = f.read()
  28.                     js = epub.EpubItem(uid=file, file_name=file, content=content,
  29.                                        media_type='application/javascript')
  30.                     js_files.append(css)
  31.     # 存储文本文件
  32.     chapter_files = []
  33.     # 遍历文件夹中的所有文件
  34.     for root, dirs, files in os.walk(input_folder):
  35.         for file in files:
  36.             if file.endswith('.html'):
  37.                 file_path = os.path.join(root, file)
  38.                 chapter = epub.EpubHtml(title=file[5:-5], file_name=file)
  39.                 with open(file_path, 'r', encoding='utf-8') as f:
  40.                     chapter.content = f.read()
  41.                 # 添加CSS和JS文件链接到HTML页面中
  42.                 for css in css_files:
  43.                     chapter.add_item(css)
  44.                 for js in js_files:
  45.                     chapter.add_item(js)
  46.                 # 在书籍中添加处理好的HTML页面
  47.                 book.add_item(chapter)
  48.                 chapter_files.append(chapter)
  49.                 # 添加目录项,可以将链接文本进行处理(这里删除了文件名前缀及文件扩展名)
  50.                 book.toc.append((epub.Link(file, file[5:-5]), file))
  51.     # 处理导航
  52.     book.add_item(epub.EpubNcx())
  53.     # 生成目录文件,uid='nav'是符合epub3规范的目录文件属性
  54.     nav = epub.EpubHtml(uid='nav', title='目录', file_name='nav.xhtml', media_type='application/xhtml+xml')
  55.     # 根据HTML文件列表生成目录导航字符串
  56.     nav_content = ''
  57.     for chapter in chapter_files:
  58.         nav_content += f'<a href="{chapter.file_name}">{chapter.title}</a><br>'
  59.     # 设置目录导航文件的内容,不设置的话book.add_item(nav)会报错Document is empty
  60.     nav.content = nav_content
  61.     book.add_item(nav)
  62.     # 添加CSS和JS文件到书籍中
  63.     for css in css_files:
  64.         book.add_item(css)
  65.     for js in js_files:
  66.         book.add_item(js)
  67.     # 假设封面图片在与html文件同一文件夹下,名为cover.jpg
  68.     cover_image_path = os.path.join(input_folder, 'cover.jpg')
  69.     if os.path.exists(cover_image_path):
  70.         with open(cover_image_path, 'rb') as f:
  71.             cover_image = epub.EpubImage(uid='cover', file_name='cover.jpg',
  72.                                          media_type='image/jpeg', content=f.read())
  73.         # 下面这行代码不注释,会报cover.jpg文件名重复错误,应该是book.set_cover()也会做添加资源的动作
  74.         # book.add_item(cover_image)
  75.         book.set_cover('cover.jpg', cover_image.content)
  76.    
  77.     # 设置书籍的spine(书脊),将封面与目录也添加进来。对于epub3.0规范来说不设置的话制作出的书籍无法阅读。列表中可以用书籍章节的uid,例如下面的封面。
  78.     book.spine = ['cover', nav] + chapter_files
  79.     # 保存书籍为EPUB文件
  80.     epub.write_epub(output_file, book, {})
  81. if __name__ == '__main__':
  82.     # 输入文件夹和输出文件名
  83.     input_folder = 'E:\\projects\\OpenCVStudy\\htmldemo\\output'
  84.     output_file = 'E:\\projects\\OpenCVStudy\\htmldemo\\何逊集校注.epub'
  85.     # 调用函数生成EPUB文件
  86.     make_epub(input_folder, output_file)
复制代码

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表