使用python批量处理epub电子书

时间:2024-03-08 10:08:53

起因

我算是一个资源收集*者,尤其是特别喜欢收集自己觉得好的电子书,多半都是epub格式,因为觉得这种格式既美观又方便,比pdf之类的好多了。

有些上传到网盘的电子书,为了避免因为侵权被删除,文件名都被上传者做了拼音化处理,这样下载下来后我们自己查看的时候,就不太方便,无法通过文件名直观的看出这本书的名字,为了解决这个问题,就需要对电子书进行重命名。

如果要处理的电子书比较少,只有两三本,那自己手工来改就行,可如果像我一样,手中有三千本电子书,其中的几百本都需要改名,那手工改的效率就太低了,这个时候,就该使用脚本来帮助我们完成了。

思路

关于如何将拼音改成汉字名称这件事,一开始我并没有太好的方法,毕竟我虽然可以通过目测推断出这本书的书名,但是我不想亲自动手啊,这种重复劳动的工作,当然是交给脚本来完成呀。

可是脚本不如我聪明,它怎么能知道这一行拼音,对应的是哪一个汉字书名呢?

我甚至想到过,先去豆瓣将所有能找到的书籍名字都爬取一遍,然后将这些名字转换成拼音,再去跟我的拼音化的电子书名相比较,超过一定相似度的,我们就认为他们是同一本书,再将其改名成相应的名字。

这个方法想想就不是什么简单的事情,简直是杀鸡用牛刀的典范,也因为爬取豆瓣所有的书名实在不是一件简单的事情,我光想想就快放弃了,根本没有行动的打算。

后来恍然间发现,在电子书阅读器上,哪怕是已经拼音化的书名,仍然能显示出正常的书名,这是怎么回事呢?

仔细一想,一定是因为epub这种格式里,包含了一定的书籍信息,比如书名,作者,出版社等信息,那么,只要用从epub中读取的书名,重命名给拼音化的书名,不就能完成我的任务了吗。

这种方法既简单,效果又好,而且想想就知道不会太难,是解决这件事情的最好方法,我又有了行动的动力,那就开始干吧。

行动

最方便的脚本语言当然就是python了,我先搜一搜python中有没有专门处理epub的包,不出所料,果然有,名字叫ebooklib。

image-20201214200421168

网上关于这个库的信息比较少,中文资料也只有寥寥几条,好在这个库足够简单,看一下别人的经验,自己再摸索摸索,足够我们解决问题了。

python真的简单,我正在学,虽然没有系统的学过,但是已经可以实现我的小想法了,下面就开始写代码吧。

import os
from ebooklib import epub
# 电子书所在路径
bookpath = \'c:\\users\\vihv\\books\\\'
renum = norenum = errornum = 0
# 设置为当前路径
os.chdir(bookpath)
# 读取所有电子书名
allbooks = os.listdir(bookpath)
for bookname in allbooks:
   # 判断是不是epub格式
   nametext, isepub = os.path.splitext(bookpath+bookname)
   if isepub == \'.epub\':
       try:
           print(\'原书名是:\', bookname, \'\n\')
           book = epub.read_epub(bookpath+bookname)
           newbookname = book.get_metadata(\'DC\', \'title\')[0][0]+\'.epub\'
           print(\'新书名是:\', newbookname, \'\n\')
           if bookname != newbookname:
               os.rename(bookname, newbookname)
               print(\'改名成功了O(∩_∩)O哈哈~\')
               renum += 1
           else:
               print(\'这本书不需要改名\')
               norenum += 1
       except:
           print(\'这里出错了,怎么回事我也不知道::>_<::\')
           errornum += 1
print(f\'任务成功结束,改名{renum}本,{errornum}本出现错误,{norenum}本不需要改名。\')

运行两遍之后,脚本的运行结果是:

image-20201214203025223

真棒,30行代码就完成了全部的工作,虽然有一些未知原因的错误,使得脚本并非十全十美,这可能是因为电子书在制作期间,就没有完全遵循正确的格式,所以我们的脚本也处理不好。但是已经很棒了,生活嘛,总要允许一点不完美的存在。

接下来我们看一下这短短的30行代码分别做了什么。温故知新,作为总结。

import os
from ebooklib import epub

这两行就是导入包嘛,os包是python和系统交互的包,我们需要这个包来和我们的系统打交道,让系统告诉我们,我们存在电子书的路径下,都有哪些电子书,他们的书名分别是什么。

ebooklib包是我们自己安装的,用来处理epub文件的包,我们从这个包里,导入epub对象。

如果你没有这个包的话,要提前使用pip来安装,安装的方法是:

pip install ebooklib

接下来是设置一些变量,代码是这样的:

# 电子书所在路径
bookpath = \'c:\\users\\vihv\\books\\\'
renum = norenum = errornum = 0
# 设置为当前路径
os.chdir(bookpath)

bookpath是存放电子书的路径,可以看到,我的电子书存放在我的用户目录vihv的books文件夹下,为什么是两条\'\\\'呢,因为在python和很多编程语言下,\都是作为转义字符存在的, 比如\n作为换行等等,这个时候\就不是\了。所以如果要表示\ ,就要用两条\\

如果你要照抄我的代码的话,就需要将这个路径换成你自己的存放电子书的路径。

然后我设置了三个全局变量,全都是0,我英文不好,这三个变量的名字十分丑陋,将就着看吧O(∩_∩)O~。

renum是rename_num的缩写,表示已经重命名的文件的数量,前边加个no就是norenum,表示不需要改名的文件的数量,errornum自然就是出现未知错误的文件的数量。

然后使用os包里的chdir方法,进入到我的电子书的路径,就相当于命令行里的cd命令。

# 读取所有电子书名
allbooks = os.listdir(bookpath)

然后我们调用os包里的 listdir 方法,这个方法以一条路径作为参数,返回一个列表,列表里是所有的文件的名字。

可以看到,我们将读取到的列表,放入了 allbooks 变量里。

python创建变量不需要提前声明,想什么时候创建都可以,这太方便了,我爱动态语言。

接下来是一个 for ... in ... 的循环,python的的 for 循环和 c 、js 等语言不同,它更加的方便,只需要一个 for ... in ... ,就会自动遍历列表里的所有内容,省去了自己计数的麻烦。

for bookname in allbooks:
   # 判断是不是epub格式
   nametext, isepub = os.path.splitext(bookpath+bookname)

可以看到,使用 for ... in ... 之后,我们就创建了一个变量 bookname ,其中就已经存放了一个书名,这个书名包含了后缀名,格式是这样的:“自学是一门手艺.epub” 。

然后我们调用os包的路径切割方法os.path.splitext,将书名的名字和后缀名分割开。它会接收一个文件路径作为参数,返回两个值,第一个值是文件名,第二个值是后缀名,其中后缀名是包含了“点”的,比如\'.epub\'。

然后根据这个后缀名,我们就可以做出判断了,如果这个后缀名等于\'.epub\',我们就知道它是一个epub格式,反之,它就不是一个epub格式。

因为我的文件夹里也有很多pdf,mobi之类格式的电子书,并不都是epub,这些文件暂时我不想处理,所以做这样的筛选是有必要的。

接下来的一段,逻辑其实很简单,就是判断这本书是不是epub格式的,如果不是,就不管它。

如果是,就看看它现在是什么名字,然后读取epub里的书名,两相对比,看看他们是不是一样的,如果是一样的,我们就不用改了,不用进行重命名这个操作,这样程序执行比较快,也会比较节省系统资源。

毕竟我们的脚本可能不会只运行一次,以后有新书加进来的时候,我们可能还会执行一次脚本,那时候很多书的名字其实已经改好了,如果再改一次的话,就多此一举了,对吧。

如果两相对比不一样,我们就用epub里的书名对它进行重命名。

    if isepub == \'.epub\':
       try:
           print(\'原书名是:\', bookname, \'\n\')
           book = epub.read_epub(bookpath+bookname)
           newbookname = book.get_metadata(\'DC\', \'title\')[0][0]+\'.epub\'
           print(\'新书名是:\', newbookname, \'\n\')
           if bookname != newbookname:
               os.rename(bookname, newbookname)
               print(\'改名成功了O(∩_∩)O哈哈~\')
               renum += 1
           else:
               print(\'这本书不需要改名\')
               norenum += 1
       except:
           print(\'这里出错了,怎么回事我也不知道::>_<::\')
           errornum += 1

可以看到,重命名的时候,使用的是os包的rename方法,这个方法接收两个参数,第一个是原名,第二个是要修改成的名字,也是十分简单啦。

这里要注意的是epub包的方法,我们使用epub包的read_epub方法,从名字上就可以看出来,这个方法就是读取epub嘛,它接收一个文件路径作为参数,返回一个epub对象。

这里我们使用book这个变量,接收了它返回的对象。

然后我们使用get_metadata方法,获取了epub里包含的书名,这个方法接收两个参数,第一个参数字符串\'DC\',第二个参数可以是title标题,creator作者,date日期等等。

’DC‘代表什么意思呢?网上搜到的信息如下:

image-20201214212307225

到这里我们就已经读取到了epub里的书名,要注意的是,get_metadata返回的是一个包含在数组里的元组,元组里包含的才是字符串,所以我在它后面加了两个0,表示我要的是这个数组里的0号元组里的0号字符串。

别忘了后缀名哦,在后面加上\'.epub\'。

然后再简单的进行比较操作之后,我们的逻辑判断部分就已经完成了,需要注意的是,这里我使用了 try ... except ... 语句,这条语句是专门用来处理异常的。

因为脚本总会因为各种意向不到的原因导致意外退出,比如一本书的格式不对劲,脚本就可能执行不下去,导致我们的任务并不能全部完成,这时候加上这条语句,脚本的健壮性就会提升不少,哪怕有一百多本书格式不对,我们的脚本依然正常运行。

接下来是最后一条语句,就是统计信息啦,有了这条语句,我们对脚本做了什么,经历了多少异常,就了然于胸了。

print(f\'任务成功结束,改名{renum}本,{errornum}本出现错误,{norenum}本不需要改名。\')

结语

python 真是太方便了,经过简单的学习之后,就可以处理很多手工不方便处理的问题,以后我们都要好好学习python,把生活变得更加自动化,偶耶!