Calibre 使用教程之抓取网站页面制成电子书
之前书伴曾写过一篇文章《Calibre 使用教程之抓取 RSS 制成电子书》,介绍了利用 Calibre 的“抓取新闻”功能把网站的新闻源制期刊样式电子书的方法。不过软件界面上也只提供了直接添加 RSS 地址的方法,也就是说网站必须有 RSS 供稿才行,否则就无法抓取。那对于不提供 RSS 的网站是否能够抓取它上面的内容制成电子书呢?本文就来介绍一种进阶技巧来解决这个问题。
在开始具体步骤之前,先简单的描述一下工作流程:首先编写一个 Calibre Recipe 脚本文件,根据 Calibre 指定的规范定义具体的抓取行为,然后使用 Calibre 把此脚本转化成 mobi 格式电子书文件。
注意,本文的相关操作是在命令行中进行的,并且会牵涉到简单的代码编写,为了让更多没有编程基础的小伙伴能直接上手使用,本文会尽可能详细的解释每一条代码的作用,以便套用。
一、认识 Calibre Recipe 脚本
Recipe 这个单词的含义为“食谱”、“处方”,顾名思义,它为 Calibre 定义了抓取新闻源这一动作的执行细节。Calibre 也为 Recipe 脚本提供了一份详尽的文档“API documentation for recipes”,对所能使用的参数或函数做了详细的说明。如果你有编程基础,可能感觉直接查看它的源代码会更清晰一些。
在抓取 RSS 制成电子书那篇文章中,我们只需要在 Calibre 软件界面上,通过“添加自定义新闻源(Add or edit a custom news source)”菜单项调出操作面板,在里面添加 RSS 地址就完事儿了,剩下的抓取、转换工作就全部交给 Calibre 自动处理了。其实在这个过程的背后,Calibre 也是根据你添加的 RSS 地址自动生成了一个 Recipe 脚本,并根据此脚本抓取内容的。可以点击“添加自定义新闻来源”操作面板左下角的【切换到高级模式】(Switch to advanced mode)按钮,便可以看到如下所示代码:
#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import unicode_literals, division, absolute_import, print_function
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1504764482(BasicNewsRecipe):
title = 'My news source'
oldest_article = 7
max_articles_per_feed = 100
feeds = [
('科学松鼠会', 'http://songshuhui.net/feed'),
('泛科学', 'http://pansci.tw/feed'),
]
从以上这个简单的 Recipe 脚本中,我们可以看到此脚本继承了 Calibre 提供的 BasicNewsRecipe 这个类,并简单的重写了一下这个类的某些参数。由于 Calibre 可以自动处理标准的 RSS 结构,所以不需要我们额外修改就可以轻松抓取内容。但是对于不提供 RSS 的网站内容又该如何处理呢?
对于不提供 RSS 的网站,我们可以通过解析页面内容,获取一个数据结构再进行转换。Calibre Recipe 脚本提供的 parse_index()
方法就可以用来做这件事。下面我们就来编写一个简单的 Recipe 脚本。
提示:Calibre Recipe 脚本的 parse_index()
方法需要解析网站页面的代码结构来提取数据,但由于不同网站的代码结构也不相同,从而处理逻辑也会有所差异,所以抓取不同的网站内容,可能就需要写一个与之相对应的 Recipe 脚本。
二、编写 Calibre Recipe 脚本
下面以王垠的博客“当然我在扯淡”为例,编写一个 Recipe 脚本,将整个博客内容转制成 mobi 格式的电子书。这个博客页面结构比较简单,个人感觉比较适合上手,初步了解一些基本的 Recipe 脚本写法。
在开始编写代码之前我们先来分析一下这个博客的页面结构:博客的首页即是全部文章列表,列表中每一篇文章的标题被被类选择器 li.list-group-item 包裹着。这样我们就可以提取出所有文章的标题和文章链接,并据此循环处理每一篇文章内容,组合成可供 Calibre 转换的数据结构。
下面是可用的 Recipe 脚本代码,代码中每一行都做了注释。看不懂可以看下面的详细解释。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from calibre.web.feeds.recipes import BasicNewsRecipe # 引入 Recipe 基础类
class Wang_Yin_Blog(BasicNewsRecipe): # 继承 BasicNewsRecipe 类的新类名
#///////////////////
# 设置电子书元数据
#///////////////////
title = '当然我在扯淡' # 电子书名
description = u'王垠的博客' # 电子书简介
#cover_url = '' # 电子书封面
#masthead_url = '' # 页头图片
__author__ = '王垠' # 作者
language = 'zh' # 语言
encoding = 'utf-8' # 编码
#///////////////////
# 抓取页面内容设置
#///////////////////
#keep_only_tags = [{ 'class': 'example' }] # 仅保留指定选择器包含的内容
no_stylesheets = True # 去除 CSS 样式
remove_javascript = True # 去除 JavaScript 脚本
auto_cleanup = True # 自动清理 HTML 代码
delay = 5 # 抓取页面间隔秒数
max_articles_per_feed = 999 # 抓取文章数量
#///////////////////
# 页面内容解析方法
#///////////////////
def parse_index(self):
site = 'http://www.yinwang.org' # 页面列表页
soup = self.index_to_soup(site) # 解析列表页返回 BeautifulSoup 对象
links = soup.findAll("li",{"class":"list-group-item title"}) # 获取所有文章链接
articles = [] # 定义空文章资源数组
for link in links: # 循环处理所有文章链接
title = link.a.contents[0].strip() # 提取文章标题
url = site + link.a.get("href") # 提取文章链接
a = {'title': title , 'url':url} # 组合标题和链接
articles.append(a) # 累加到数组中
ans = [(self.title, articles)] # 组成最终的数据结构
return ans # 返回可供 Calibre 转换的数据结构
首先引入 Calibre 提供的基础类 BasicNewsRecipe
并创建一个继承基础类的新类 Wang_Yin_Blog
。
接下来重写一些可作为电子书的元数据的参数。如标题、简介、作者、语言、编码之类。注意上面代码中 cover_url
和 masthead_url
这两个参数被注释掉了,这样 Calibre 会自动生成封面和期刊头。如果你想要自定义电子书封面和期刊头,可以使用这两个参数指定图片的路径。
然后还需要设置控制抓取页面所需要的一些参数。如去除电子书不需要的 CSS 样式和 Javascript 脚本,设定抓取页面的时间间隔(避免对目标服务器造成负担),设定抓取文章的数量(如果想要抓取所有文章设置一个足够大的数值即可)等。注意以上代码中有一个 auto_cleanup
参数,它会用可读性算法自动清理 HTML 标签提取页面中的有用内容。如果页面内容比较复杂,还可以使用 keep_only_tags
这个参数,指定仅提取页面中某个标签中的内容,因为本例页面内容较简单就注释掉了。
相关参数设置完毕后,就可以编写处理页面内容的 parse_index()
方法了。在此方法中 Calibre 使用了内置的 Python 模块 BeautifulSoup。首先把首页的文章列表解析成 BeautifulSoup 对象,然后提取出所有标题列表,循环处理这些列表后,最终合并成一个完整的数据结构交给 Calibre 转换处理。
这样一个简单的 Recipe 脚本就写完了,将其保存为 .recipe 文件备用,本例保存为 wangyin.recipe。接下来就可以把这个“小处方”转换成 mobi 格式的电子书文件了。
提示:当然有些网站的情况要复杂得多,比如处理带分页的页面、复杂内容类型,还有多内容来源的合并等,这些进阶技巧限于篇幅暂不展开。如果感兴趣,也可以翻一翻 Calibre 提供的 API 文档“API documentation for recipes”自行研究一下。
三、认识命令行工具 ebook-convert
有了写好的 Recipe 脚本,接下来的工作就是将其转化成 mobi 格式的电子书文件了。
在《Calibre 使用教程之批量获取电子书元数据》这篇文章中,我们认识了 Calibre 的一个命令行工具 ebook-meta
,它可以获取电子书的元数据。现在要接触到另外一个命令行工具 ebook-convert
,此工具可以把某种格式转换成另一种格式。比如想要把某个 epub 转换成 mobi,只需要输入以下命令即可:
ebook-convert BookName.epub BookName.mobi
当然想要使用 ebook-convert
命令需要预先在电脑里安装 Calibre。在 Windows 系统中,一般安装完成后即可直接在“命令提示符”中使用。对于 macOS 系统则需要设置一下环境变量,设置方法和 ebook-meta
一样,参考《Calibre 使用教程之批量获取电子书元数据》这篇文章中的“准备 ebook-meta 工具”。
四、把 Recipe 脚本转化为 mobi 文件
和转换普通的电子书的格式一样,只需要输入以下命令即可开始进行转化。转换所需要的时间和文章条目和网速相关,如果你抓取的站点不幸被墙了,还需要使用网络代理。
ebook-convert wangyin.recipe wangyin.mobi --output-profile kindle
注意上面的代码中增加了一个参数 --output-profile kindle
,这个参数的用途是将生成的电子书文件为入门版 Kindle 设备做适配。如果不添加这个参数,转换出来的电子书会有一个对 Kindle 来说多余的翻页导航和额外信息。除了可适配入门版 Kindle 设备外,你还可以为不同型号的 Kindle 设备选用与之相应的参数值进行适配,如 kindle_dx
、kindle_oasis
、kindle_pw
、kindle_pw3
和 kindle_voyage
。
另外在转换的过程中也会有意外情况,比如由于资源链接被墙,或由于网络不稳定导致页面抓取失败。本例中抓取的博客由于引用了两张 Google 服务器上的图片,不使用代理就会抓取失败。
以上命令执行完毕后便可以得到最终的电子书文件 wangyin.mobi,拷贝或推送到 Kindle 即可阅读。
提示1:如果你不想使用命令行工具,当然也可以使用 Calibre 界面上的“抓取新闻”功能来完成同样的工作。你只需要把编写好的 Recipe 代码粘贴到新建的 Recipe 脚本中,或者直接导入已有的 Recipe 脚本文件,然后和抓取 RSS 的操作一样,在“定期新闻下载”面板上选中“自定义脚本”,点击【立即下载】按钮即可完成转换。不过这种方法会始终带有翻页导航。
提示2:当然还有一个比较方便的转换方法,就是直接把 Recipe 脚本拖进 Calibre,然后像转换普通电子书那样进行转换,Calibre 会自动执行抓取工作,最终将抓取的数据转成目标格式。
五、现成的 Calibre Recipe 脚本
除了自己手动针对某个网站的内容编写 Recipe 脚本外,对于一些知名度较高的站点,已经有很多现成的 Recipe 脚本可用,比如 Calibre 项目自身就提供了一个 Recipe 脚本库(Calibre 的“抓取新闻”内置的那些就是使用的这些 Recipe 脚本)。另外也有很多网友也分享了自己编写的的 Recipe 脚本,你可以访问 GitHub 搜索关键字“calibre recipe”来查找感兴趣的脚本。当然也欢迎你的分享。
以上就是利用 Recipe 脚本抓取不提供 RSS 的网站内容并制成电子书的方法。以上内容尽量兼顾没有任何编程经验的小伙伴,如果按照你的理解方式对那些地方不太明白,请留言,确认有误区后会按照你的意见进行更改。如果你发现本文存在错误,也欢迎留言指正。有更好的玩儿法,也欢迎分享。
参考资料:
- API documentation for recipes
- Adding your favorite news website
- calibre/src/calibre/web/feeds/news.py
- Beautiful Soup Documentation
- ebook-convert
- 用calibre抓取乌云知识库并生成电子书
- 抓取在线网页,利用 Calibre 生成 mobi 电子书
- 抓取网页内容生成Kindle电子书
- 使用calibre多卷抓取《随园食单》
- 利用Calibre.recipe爬取文章
© 「书伴」原创文章,转载请注明出处及原文链接:https://bookfere.com/post/562.html
“制作Kindle电子书”相关阅读
- GitBook 制作 Kindle 电子书详细教程(可视化版)
- 乐书:在线 Kindle 电子书制作和转换工具
- 亚马逊 Kindle 电子书发布指南(三)特定类型电子书指南
- 亚马逊 Kindle 电子书发布指南(二)一般最佳实践
- 亚马逊 Kindle 电子书发布指南(四)附录
- Kindle Comic Converter:最简单的漫画转换工具
- KF8 格式电子书支持的 HTML 标签和 CSS 属性
- 如何为 Kindle 电子书添加嵌入多种自定义字体
- Calibre 使用教程之抓取 RSS 制成电子书
- Kindle 漫画制作软件 ChainLP 简明教程
- 制作 KF8 标准电子书示例(下):图片与背景
- 遵循亚马逊标准!Kindle 电子书专业制作教程
- GitBook 制作 Kindle 电子书详细教程(命令行版)
- 如何给自制 Kindle 电子书添加弹出脚注或尾注
- Sigil 进阶教程:从零开始制作 EPub 电子书
你好,想请教下,某些章节(article)会下载失败,在浏览器上单独打开该链接是正常的,重新生成电子书该章节也正常,但其它章节可能又失败了,有没有办法对章节下载设置失败重试次数?单独把timeout 改大没有用。
好像没有相关的设置。不过你可以通过覆写相关的方法来控制请求的重试:
非常感谢,现在可以了
抓取新闻以后的书名title怎么样设置为书名[星期,日 月 年]这样的格式?之前3.2版本是自动可以搞出来的,到了5.81就不知道怎么设置了。😂研究了半天都没看到哪里有设置的……
可以修改脚本,添加这样一行代码
timefmt = ' [%a, %d %b %Y]'
。怎样抓取需要登录才能完整浏览的页面呢(有账号密码)
可以查看 Calibre 的 news.py 源代码,参考 487 行的
get_browser()
函数,注释提供了示例用法,你可以参考它在你自己的脚本里重写这个函数以实现登录功能。请问https://chuansongme.com/account/m15875 这里的文章怎么采集,老是没有成功,谢谢。
github 上 Calibre 的 python3 版本要出来了,站长会更新教程吗?
即便 Calibre 升级到 Python3,对 Recipe 脚本的影响应该不会很大,如果有必要就会更新。
我曾试过用 4.x 版本抓取网站内容,感觉并没有旧版本效果好,比如有时候,程序会认为页面上有些图片格式不符合标准,强行忽略,而 3.x 就比较宽容。
抓取这个网页
https://www.gamersky.com/handbook/201512/692422_181.shtml
只有文字没有图片
我写的代码如下
页面使用了图片 Lazy Load 技术,你需要对其做处理。以你提供的 Recipe 为例,只需要重写 preprocess_raw_html() 函数即可:
哇,想不到这么老的帖子博主还在回答。请教一个让我快头烂的问题:
我写了一个recipe,集合了好几个源,源是自己在国外主机搭建的rsshub抓的instagram。在rss reader上是能正常看的,但是写到recipe里抓的只有标题没有图片。
fetch报错一堆如下
Forcing feed_5/article_3/index.html into XHTML namespace
COLOR_VALUE: Missing token for production Sequence(comma, Choice(unary +-, number, percentage)): (u’FUNCTION’, u’var(‘, 1, 155)
COLOR_VALUE: Missing token for production end FUNC “)”
PropertyValue: No match: (u’CHAR’, u’-‘, 1, 159)
PropertyValue: Unknown syntax or no value: rgba(var(–i1d,38,38,38),1)
CSSStyleDeclaration: Syntax Error in Property: color:rgba(var(–i1d,38,38,38),1)
COLOR_VALUE: Missing token for production Sequence(comma, Choice(unary +-, number, percentage)): (u’FUNCTION’, u’var(‘, 2, 114)
COLOR_VALUE: Missing token for production end FUNC “)”
PropertyValue: No match: (u’CHAR’, u’-‘, 2, 118)
PropertyValue: Unknown syntax or no value: 1px solid rgba(var(–b38,219,219,219),1)
CSSStyleDeclaration: Syntax Error in Property: border:1px solid rgba(var(–b38,219,219,219),1)
COLOR_VALUE: Missing token for production Sequence(comma, Choice(unary +-, number, percentage)): (u’FUNCTION’, u’var(‘, 2, 246)
COLOR_VALUE: Missing token for production end FUNC “)”
PropertyValue: No match: (u’CHAR’, u’-‘, 2, 250)
PropertyValue: Unknown syntax or no value: rgba(var(–h5f,56,151,240),1)
CSSStyleDeclaration: Syntax Error in Property: background-color:rgba(var(–h5f,56,151,240),1)
COLOR_VALUE: Missing token for production Sequence(comma, Choice(unary +-, number, percentage)): (u’FUNCTION’, u’var(‘, 2, 326)
COLOR_VALUE: Missing token for production end FUNC “)”
PropertyValue: No match: (u’CHAR’, u’-‘, 2, 330)
PropertyValue: Unknown syntax or no value: rgba(var(–fa7,223,239,255),1)
CSSStyleDeclaration: Syntax Error in Property: background-color:rgba(var(–fa7,223,239,255),1)
COLOR_VALUE: Missing token for production Sequence(comma, Choice(unary +-, number, percentage)): (u’FUNCTION’, u’var(‘, 2, 406)
请问有啥办法能抓到图片
从你提供的日志来看,都是些解析 CSS 相关的信息,和图片的抓取没有什么关系。建议你在使用 Calibre 执行 Recipe 时添加一个参数
-vv
,以获取更详尽的日志信息,看有没有和抓取图片相关的信息。您好!
http://www.guoxue123.com/zhibu/0101/04zy/index.htm
像这个网页,只有一个子层级,下载并转换成带目录的电子书呢?
我复制了您的代码编辑后,在calibre中获取新闻,但是只能下载一个首页,
不能下载二级子层级页面……
另外,正文中多了 “^l^l ” 这样的换行,不知可否批量处理?
http://www.guoxue123.com/zhibu/0101/04zy/000.htm
感谢!
这个页面的结构不适用文章中给出的示例。这种页面的 HTML 结构比较散乱,需要自己写算法,但是为了不至于太麻烦,你可以根据具体情况做一些变通。下面是根据你提供的 URL 编写的一个可用脚本,思路是先手工提取出章节,然后分析文章 URL 中的序号并根据章节制定范围,这样就可以直接抓取页面上的全部 URL,在循环时根据制定的那个范围来判断文章从属哪个章节:
不过这个脚本只是把页面上的内容简单抓取下来了,排版效果不怎么好,如果想近一步优化排版,就需要学学 BeautifulSoup 了。
#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import unicode_literals, division, absolute_import, print_function
from calibre.web.feeds.news import BasicNewsRecipe
class rdzs(BasicNewsRecipe):
title = ‘儒道至圣’
description = ‘这是一个读书人掌握天地之力的世界。 才气在身,诗可杀敌,词能灭军,文章安天下。 秀才提笔,纸上谈兵;举人杀敌,出口成章;进士一怒,唇枪舌剑。 圣人驾临,口诛笔伐,可诛人,可判天子无道,以一敌国。 此时,圣院把持文位,国君掌官位,十国相争,蛮族虎视,群妖作乱。 此时,无唐诗大兴,无宋词鼎盛,无创新文章,百年无新圣。 一个默默无闻的寒门子弟,被人砸破头后,挟传世诗词,书惊圣文章,踏上至圣之路。’
max_articles_per_feed = 20000
fileName = ‘xx\rdzs.txt’
cover_url = ‘http://www.50zw.la/files/article/image/2/2806/2806s.jpg’
no_stylesheets = True
keep_only_tags = [dict(name=’div’, attrs={‘class’:’h1title’}),dict(name=’div’, attrs={‘id’:’htmlContent’})]
url_prefix = ‘http://www.xxbiquge.com’
no_stylesheets = True
keep_only_tags = [dict(name=’div’, attrs={‘class’:’bookname’}),dict(name=’div’, attrs={‘id’:’content’})]
file_object = open(fileName,’r’)
lastHref = file_object.read()
file_object.close()
hasLoad = bool(lastHref)
def get_title(self, link):
return link.contents[0].strip()
def parse_index(self):
soup = self.index_to_soup(self.url_prefix+”/5_5690″)
div = soup.find(‘div’, { ‘id’: ‘list’ })
lastHref =self.lastHref
articles = []
for link in div.findAll(‘a’):
til = self.get_title(link)
href = link[‘href’]
self.lastHref = href
if href == lastHref:
self.hasLoad = False
if self.hasLoad:
continue
else:
url = self.url_prefix + href
a = { ‘title’: til, ‘url’: url }
articles.append(a)
tutorial = [(self.title, articles)]
file_write = open(self.fileName,’w’)
file_write.write(self.lastHref)
file_write.flush()
file_write.close()
return tutorial
出错 filename
请问能不能设定爬取的间隔时间?有些网站最多只能爬取30个页面
可以直接用 time 模块中的函数 time.sleep(secs) 在循环中实现时间的延迟。比如 time.sleep(5) 表示间隔 5 秒。
请问有没有办法把calibre抓的Taipei Times上的图片放大,使图片能够填满屏幕,就像经济学人上的图片似的,可能是由于图片像素太小吧!放到kindle上看图片总是缩到左边。
每章后面都有的一句话,有没有办法删掉呢
This article was downloaded by calibre from xx
解决方法就是在转换时添加参数
--output-profile
,建议仔细阅读本文第四部分内容,已有说明。