[python]用爬虫下载某站小说并生成epub格式电子书(用mkepub库)

参考这篇文章

最近沉迷看网络小说,苦于部分网站用手机浏览器看很不方便、不能同步阅读进度,更重要的是在电梯车库等地方手机都没信号,因此准备用爬虫爬下来看.

其实TXT版本的电子书比较好找,但是没有目录什么的,很难受.因此选择epub格式的电子书,对于kindle用户可以用calibre转换成mobi或直接使用官方转换推送一条龙服务.

前面的参考博客里推荐的是使用pandoc软件实现有格式的TXT文件转换至epub,挺简单,但是没有首行缩进很不舒服.

研究了很久pandoc的模板和css,也没实现首行缩进,最终决定换个方式.

pandoc会把正文放进code标签内,找不到实现缩进的css代码,如果有办法实现,也可以通过自定义css的方式.

搜索许久找到一个介绍还算比较清楚的库mkepub,记录一下踩到的坑.

从网站爬内容,在参考博客里写的很清楚,不再多说,根据网站实际情况清洗就好了.

爬到正文以后,先将NBSP替换成空格,之后根据实际情况拆分成列表.
爬取和清洗的代码如下:

    def _get_web(self,url):
        res=S.get(url) # S是requests.session()
        res.encoding='gbk' # 该网站编码是'ISO-8859-1',会有乱码
        soup=BeautifulSoup(res.text, "html.parser")
        return soup

       def _anl_web(self,this_url:str):
        url=self.url_basic+this_url
        soup=self._get_web(url)

        title=soup.select('#content > div > div.h1title > h1')[0].getText() # select 参数根据实际情况确定
        content = soup.select('#htmlContent')[0].getText().split('\r\n')[1].replace(' ',' ').strip().split(' ') # replace第一个参数实际上是 NBSP

        next=soup.select('#pager_next')[0]
        next_url = next.get('href')
        next_title = next.getText() # 该网站末页的标题是特殊固定的,用于判断结束
        return [title,content,next_url,next_title]

需要注意的是,bs4的select返回值是一个列表,需要先取值.因为网站格式统一,一般不会出现空列表的情况,所以就没做判断.

还需要获取书的meta数据,根据网站不同,获取方式也不相同,不过大体类似

到这一步后,参考博客里是将爬到的数据写进了txt文件,我这里是写进了xhtml文件.

XHTML_SMP="""<section id="{title}" class="level1" data-number="{no}"> <h1 data-number="{no}">{title}</h1> {body} </section> """

这是一个xhtml文件的模板,大概就是解包了一个epub,从里面抄的,需要注意的是,mkepub会自己加上html,head,body等内容及标签,因此只需要保留body内的部分就好.

模板里用花括号包裹的内容,是利用python的format字符串替换的内容.替换方式见后.

写入文件部分代码如下:

no=0
xhtml_list=[]
while self.flag:
    no+=1
    xhtml_path:Path=self.dirpath/('ch'+f'{no}'.rjust(5,'0')+'.xhtml') # 这里用到了path库
    title,content,url,next_title=self._anl_web(url)
    print(title) # 打印标题,表示没卡死

    if '书末页' in next_title:
        self.flag=False
    body=''
    for line in content:
        body+=f'<p>{line}</p>\n' # 每行用<p>标签包裹,方便用css定义首行缩进.加个换行符是为了源码好看,对最终实现效果没影响
    xhtml_path.write_text(XHTML_SMP.format(**locals())) # 替换模版里的变量
    xhtml_list.append([xhtml_path,title]) # 返回xhtml文件路径以及章标题,mkepub要用
return xhtml_list										

XHTML_SMP.format(**locals())的作用是用本地变量格式化字符串.需要注意的是变量名和作用域.
下面是错误示范:

def some():
	c=1

def some2():
	self.a=1
	b=2
	s='{a}{self.b}{c}'.format(**locals())

a和b是因为变量名不同,c是因为作用域不同,都会报错

之后是用mkepub制作epub文件.

import json
import mkepub
from path import Path
from com.Smp import CSS_PATH
class make_epub:
    def __init__(self,xhtml_list:[Path,str],dir_path:Path):
        self.dir_path = dir_path
        self.xhtml_list = xhtml_list

    def to_do(self):
        self._get_mate()
        book = mkepub.Book(title=self.book_name, author=self.author) # 定义书名和作者 
        book.set_stylesheet(CSS_PATH.read_text(encoding='utf8')) # 传入的是css文件内容,CSS_PATH类型是path.Path
        for xhtml_path,title in self.xhtml_list:
            book.add_page(title=title,content=xhtml_path.read_text(encoding='UTF-8'))# 传入的是文件内容,
        book.save(self.dir_path.parent/(self.book_name+'.epub'))

    def _get_mate(self):
		# 之前将mate信息写进了mate.json文件内
        mate_path:Path=self.dir_path/'meta.json'
        mate=json.loads(mate_path.read_text(encoding='utf8'))

        self.book_name=mate['book_name']
        self.author = mate['author']

css文件是抄的pandoc的默认css,加了点内容,文件内容如下:

/* This defines styles and classes used in the book */
body {  margin: 5%; text-align: justify; font-size: medium; }
code {  font-family: monospace; }
h1 {  text-align: left; }
h2 {  text-align: left; }
h3 {  text-align: left; }
h4 {  text-align: left; }
h5 {  text-align: left; }
h6 {  text-align: left; }
/* For title, author, and date on the cover page */
h1.title {  }
p.author {  }
p.date {  }
p{ text-indent: 2em;} /* 加入的内容,首行缩进两个字符 */
nav#toc ol, nav#landmarks ol {  padding: 0; margin-left: 1em; }
nav#toc ol li, nav#landmarks ol li {  list-style-type: none; margin: 0; padding: 0; }
a.footnote-ref {  vertical-align: super; }
em, em em em, em em em em em {  font-style: italic;}
em em, em em em em {  font-style: normal; }
code{  white-space: pre-wrap; }
span.smallcaps{  font-variant: small-caps; }
span.underline{  text-decoration: underline; }
q {  quotes: "“" "”" "‘" "’"; }
div.column{  display: inline-block; vertical-align: top; width: 50%; }
div.hanging-indent{ margin-left: 1.5em; text-indent: -1.5em;}
@media screen {  /* Workaround for iBooks issue; see #6242 */
  .sourceCode { 
    overflow: visible !important;
    white-space: pre-wrap !important;
  }
}

至此全部代码完成,不过实际测试的时候发现生成的epub文件有问题,提示编码错误.检查mkepub源码修改了点内容,如下:

# mkepub.py
    def _write(self, template, path, **data):
        with open(str(self.path / path), 'w',encoding='UTF-8') as file: # 加入了encoding参数
            file.write(env.get_template(template).render(**data))

之后可以正常使用.

我将整个程序分成了四部分,分别是:

  1. 入口流程main模块,用来解析url,并分配不同的rule类
  2. rule模块,对于不同的网站实际处理方式不同(现在就只完成了一个网站)
  3. make_epub模块,把xhtml文件打包成epub文件
  4. 一些辅助的小模块,例如前面用到的S的定义
    原文作者:wyl220
    原文地址: https://blog.csdn.net/wyl220/article/details/123492845
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞