上一篇EPub格式電子書的制作介紹了EPub的基本概念,并用Python制作了一本簡(jiǎn)單的EPub電子書迂烁,這次猴贰,我們做一個(gè)EPub閱讀器。
既然主題已經(jīng)限定這是一個(gè)「簡(jiǎn)單」的EPub閱讀器乔夯,我們?cè)燧喿拥倪^(guò)程自然是以「掌握核心科技」為原則砖织,怎么簡(jiǎn)單怎么來(lái)(PS1:我說(shuō)的好有道理你竟然無(wú)言以對(duì)。PS2:「什么末荐,你有話說(shuō)侧纯?」,「哦甲脏!」)
所以呢眶熬,這個(gè)EPub閱讀器的構(gòu)想是這樣的,一個(gè)主界面上一共3個(gè)Widget块请,其中兩個(gè)一個(gè)用來(lái)查看章節(jié)娜氏,一個(gè)用來(lái)查看文字內(nèi)容,文字內(nèi)容一般用html墩新,xhtml展示贸弥,那我們就用webview來(lái)展示文字的內(nèi)容,還有一個(gè)作為圖書倉(cāng)庫(kù)(Library)海渊,里面可以看到已經(jīng)在倉(cāng)庫(kù)中的圖書绵疲。
上一篇提到,EPub電子書實(shí)際上是一個(gè)壓縮包文件臣疑,而且壓縮包內(nèi)的基本結(jié)構(gòu)我們也已經(jīng)了解了盔憨,要將這些數(shù)據(jù)展示在GUI上,需要一個(gè)抽象模型存放要顯示的內(nèi)容讯沈,所以郁岩,這個(gè)模塊就叫做Books吧,它存放的信息包括書名缺狠,作者驯用,章節(jié)名以及對(duì)應(yīng)的在文件目錄中的地址,這一篇呢儒老,我們就先寫這個(gè)模塊蝴乔。
解析xml用到的模塊是BeautifulSoup4和lxml,不會(huì)的童鞋也不要緊張驮樊,這兩個(gè)模塊非常容易用薇正,花一點(diǎn)時(shí)間學(xué)一下就行了。 你看囚衔,就像這樣:
EPub格式電子書的制作中提到挖腰,META-INF/container.xml中包含了opf文件的路徑,而opf文件中有書籍的基本信息练湿,如書名猴仑,作者等等。所以,流程也很簡(jiǎn)單辽俗,先用zifile解壓讀取container.xml疾渣,再用bs4從opf文件中解析出需要的信息。首先放一本epub格式的書籍到工作目錄中崖飘,新建一個(gè)books.py榴捡,如:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import zipfile
from BeautifulSoup import BeautifulStoneSoup
LIBRARY_DIR = os.path.abspath('.') + os.sep
class Book(object):
u"""
"""
_FILE = LIBRARY_DIR + '%s.epub'
def __init__(self, book_id=None):
if book_id:
self.open(book_id)
def open(self, book_id=None):
if book_id:
self.book_id = book_id
if not self.book_id:
raise Exception('Book id not set')
self.f = zipfile.ZipFile(self._FILE % self.book_id, 'r')
soup = BeautifulStoneSoup(self.f.read('META-INF/container.xml'))
oebps = soup.findAll('rootfile')[0]['full-path']
print ("ops filename path:" + str(oebps))
folder = oebps.rfind(os.sep)
self.oebps_folder = '' if folder == -1 else oebps[:folder+1] # 找到oebps的文件夾名稱
if __name__ == '__main__':
book = Book('莎士比亞全集')
print book.oebps_folder
獲得ops文件名,所在目錄: 如:
照這個(gè)思路寫下去朱浴,得到書名吊圾,作者信息,從ncx文件中獲得目錄信息翰蠢,代碼如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import zipfile
from lxml import etree
from BeautifulSoup import BeautifulStoneSoup
LIBRARY_DIR = os.path.abspath('.') + os.sep
RECOVER_PARSER = etree.XMLParser(recover=True, no_network=True)
NAMESPACES = {
'dc': 'http://purl.org/dc/elements/1.1/',
}
class Book(object):
u"""
需要主動(dòng)調(diào)用open方法才能獲得相應(yīng)的屬性
"""
_FILE = LIBRARY_DIR + '%s.epub'
def __init__(self, book_id=None):
if book_id:
self.open(book_id)
def fromstring(self, raw, parser=RECOVER_PARSER):
return etree.fromstring(raw, parser=parser)
def read_doc_props(self, raw):
u"""
:param raw: raw string of xml
:return:
"""
root = self.fromstring(raw)
self.title = root.xpath('//dc:title', namespaces={'dc': NAMESPACES['dc']})[0].text
self.author = root.xpath('//dc:creator', namespaces={'dc': NAMESPACES['dc']})[0].text
def open(self, book_id=None):
if book_id:
self.book_id = book_id
if not self.book_id:
raise Exception('Book id not set')
self.f = zipfile.ZipFile(self._FILE % self.book_id, 'r')
soup = BeautifulStoneSoup(self.f.read('META-INF/container.xml'))
oebps = soup.findAll('rootfile')[0]['full-path']
folder = oebps.rfind(os.sep)
self.oebps_folder = '' if folder == -1 else oebps[:folder+1] # 找到oebps的文件夾名稱
oebps_content = self.f.read(oebps)
self.read_doc_props(oebps_content)
opf_bs = BeautifulStoneSoup(oebps_content)
ncx = opf_bs.findAll('item', {'id': 'ncx'})[0]
ncx = self.oebps_folder + ncx['href'] # 找到ncx的完整路徑
ncx_bs = BeautifulStoneSoup(self.f.read(ncx))
self.chapters = [(nav.navlabel.text, nav.content['src']) for
nav in ncx_bs.findAll('navmap')[0].findAll('navpoint')]
if __name__ == '__main__':
book = Book('莎士比亞全集')
print book.oebps_folder
print book.title
print book.author
print str(book.chapters).decode("unicode-escape").encode("utf-8")
結(jié)果如圖:
說(shuō)明:測(cè)試過(guò)程中發(fā)現(xiàn)有的epub圖書不規(guī)范项乒,目錄信息中沒(méi)有章節(jié)描述和對(duì)應(yīng)的路徑,對(duì)于這些書籍可能會(huì)報(bào)錯(cuò)(OS:管它呢梁沧,反正不是我報(bào)錯(cuò)(OS:WTF))檀何。