Calibre 使用教程之抓取网站页面制成电子书

“制作Kindle电子书”相关阅读

给这篇文章写一条留言

提示:带 * 标记的是必填项。您填写的邮箱地址将会被保密。首次留言将会在通过人工审核后显示。如果是提出问题,请务必提供尽可能多信息,这有助于他人更好地理解你所提出的问题。

小伙伴们写下了 72 条留言

  1. 请问有没有升级SCMP抓取方案的计划啊 现在增加预览之后 calibre内置的抓取方案不能再抓取到文本了 感谢

  2. 请问多页的文章怎么抓取啊?
    https://language.chinadaily.com.cn/5af95d44a3103f6866ee845c/
    希望有大佬帮忙做个recipe,谢谢!!

  3. 用calibre抓纽约时报的这个https://feedx.net/rss/nytimes.xml 然后成功了电脑上可以看到图片, 但到了kindle上却看不到图片。
    抓BBC这个 https://feedx.net/rss/bbc.xml calibre抓不到图片只有文字。
    抓VOA这个 https://feedx.net/rss/mgzy1.xml calibre什么都抓不着。
    以上三个rss放到 kindle4rss.com 进行抓取全都成功了而且放到kindle上也有图有文。
    不知道它这个网站的recipe与calibre的recipe有什么不同。
    不知道您知不知道calibre的recipe错在哪了,望指正,谢谢!

    • 图片不显示的问题貌似是因为 Kindle 不读取图片导致的,具体原因没找到,解决方法是给 Recipe 脚本添加一个参数 compress_news_images = True,让 Calibre 把图片压缩处理一下,就可以正常显示了。

      关于不显示图片的问题,可能是因为添加参数 auto_cleanup = True 的原因,导致处理内容时把图片给过滤掉了。对于 RSS 来说,一般不需要对内容进行清理,所以直接把这个参数去掉就可以了。

      抓不到内容是因为网络(比如墙)的问题,建议检查网络的可用性。

  4. 各位大佬有没有愿意帮忙做一下人民日报的抓取,自己尝试了一下结果完全跪了,保存都保存不了ORZ,有的话感激不尽啊.

    from __future__ import print_function
    from calibre.web.feeds.news import BasicNewsRecipe # 引入 Recipe 基础类
    from datetime import datetime, timedelta
    from calibre.ebooks.BeautifulSoup import Tag, BeautifulSoup
    from calibre.utils.magick import Image, PixelWand
    from urllib2 import Request, urlopen, URLError
    
    class PeopleDaily(BasicNewsRecipe): # 继承 BasicNewsRecipe 类的新类名
    
        date = datetime.now() - timedelta(days=2)
        pubdate = date.strftime('%a, %d %b')
        if date.hour < 10:
                date = date - timedelta(days=1)
        #///////////////////
        # 设置电子书元数据
        #///////////////////
        title = '人民日报电子版' # 电子书名
        description = u'人民日报电子版' # 电子书简介
        cover_url = 'http://paper.people.com.cn/rmrb/page/' + \hoje.strftime('%Y') + '-' + ' +   \hoje.strftime('%m')' + '/'+ \hoje.strftime('%d') + '/' + \hoje.strftime('%m') +'/rmrb'+ \hoje.strftime('%d') + '01_b.jpg' # 电子书封面
        #timefmt = '[%d %b, %Y]' #时间
        #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://paper.people.com.cn/rmrb/page/' + \hoje.strftime('%Y') + '-' + ' +   \hoje.strftime('%m')' + '/'+ \hoje.strftime('%d') + '/nbs.D110000renmrb_01.htm'' 
            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 转换的数据结构
      • 这个评论我之前也看到过,但是人民日报RSS和《人民日报 电子版》(http://paper.people.com.cn/rmrb/html/2019-01/19/nbs.D110000renmrb_01.htm)内容差别很大,请问有可能抓取这样的网站吗(我上面的代码就是对这个网站的)?

  5. 我想抓按《Qt 学习之路 2》目录抓取网页内容生成电子书,详细链接及我改的代码已经发博主邮箱,发出来感觉对源站不太礼貌……
    抓取的时候发现无法获取图片,同样的代码改改拿去测试其他网站,却能抓取成功。
    希望博主能够指点,谢谢!

    • 邮件已收到。这个问题出在图片 URL 上。

      这个网站使用了图片懒加载脚本,也就是说 img 标签中 src 属性不是真正图片 URL,真正的图片 URL 存放在 data-layzr 属性里,只有当浏览器触发了懒脚本的条件 src 属性才会被替换成 data-lazyr 中的 URL。因为 Reicpe 脚本无法触发 Javascript 所以就无法抓取到真正的图片了。

      如果能让脚本获取 data-lazyr 而不是 src 中的值就能正确抓取了。之前遇到个这个问题,但是没有找到解决方法。

    • 这种没有明显页面结构的,建议自己把目录页的代码重新编辑一下,使其结构适合 Calibre 抓取,然后抓取自己编辑的这个页面。

        • 比如你给出的这个网页的目录页面是 library.htm,先把它另存到本地,然后编辑它的结构适合 Calibre 抓取,最后编辑 recipe 的时候,设置抓取的页面为本地路径(比如 file:///Users/yourname/Desktop/library.htm)就可以了。

          • 真机智哈!
            但是问题来了,如何对each article 进行内容筛选,它的内容也很乱,老外的网页格式是什么鬼,感觉css 做出来的网页

            • 内容没有明显结构不太好处理。可以尝试在 Recipe 脚本中添加一个配置 auto_cleanup = True,让程序自动清理一下代码,看内容的可读性能不能接受。

              • from calibre.web.feeds.recipes import BasicNewsRecipe
                
                class automotiveRepair(BasicNewsRecipe):
                    title = 'automotiveRepair'
                    no_stylesheets = True
                    language = 'en'
                    max_articles_per_feed = 999999
                    simultaneous_downloads = 10
                    remove_javascript = True   
                    auto_cleanup = True
                    remove_tags = [{'class':'gadget-wrapper cleanslate'},]
                
                
                
                    def parse_index(self):
                        soup = self.index_to_soup('file:///C:\Users\supernova\Desktop/library.htm')
                        articles = []
                        div = soup.find('div', { 'class': 'gogogo' })   # 我在开始目录正文的地方放的div标签
                        
                        for link in div.findAll('a'):  
                            
                            if 'www.aa1cars.com' in link['href']:
                                title = link.contents[0].strip()   
                                print(title)  
                                url = link['href']
                                print(url)
                                articles.append( {'title': title, 'url':url})
                
                        return [(self.title, articles)]

                结果是:

                Conversion options changed from defaults:
                  test: None
                  output_profile: u'kindle'
                1% 将输入转换为HTML中...
                InputFormatPlugin: Recipe Input running
                Using custom recipe
                1% 正在抓取源...
                1% 从索引页面获取了源
                1% 正在尝试下载封面...
                1% 正在生成刊头...
                Synthesizing mastheadImage
                1% 开始下载 [10 线程]…
                34% 新闻源已下载到 C:\Users\SUPERN~1\AppData\Local\Temp\calibre_zqmwr7\cr_46c_plumber\index.html
                34% 下载完成
                Parsing all content...
                Forcing index.html into XHTML namespace
                Forcing feed_0/index.html into XHTML namespace
                34% 正在对电子书进行转换...
                Merging user specified metadata...
                Detecting structure...
                Flattening CSS and remapping font sizes...
                Source base font size is 12.00000pt
                Removing fake margins...
                Cleaning up manifest...
                Trimming unused files from manifest...
                Creating AZW3 Output...
                67% 正在运行 AZW3 Output 插件
                Serializing resources...
                Splitting markup on page breaks and flow limits, if any...
                Creating KF8 output
                        Generating KF8 markup...
                        Compressing markup...
                        Creating indices...
                Document has no ToC, MOBI will have no NCX index
                AZW3 output written to C:\Users\supernova\Desktop\auto.azw3
                输出保存到   C:\Users\supernova\Desktop\auto.azw3
              • up主,这个问题能不能解决?感谢感谢你了,这网站对我们修车的很重要,我好想在kindle上看呀!

      • 那篇文章给出了解释:使用两个循环,第一个 for 分卷,生成卷标题,第二个for 生成本卷 article,即 (title, url)list(图中的 Chapters),然后加上卷标题,生成 volume(图中的 Tuple),通过 append 把各卷组合成 ans0,即图中的 book(List),返回这个值。遇到不懂的值,可以在运行代码的时候 print 出来以便理解。

      • 谢谢你的回复 速度好快。
        代码跑了起来 但是有的书下不了

        Could not fetch link http://www.liujiangblog.com/course/django/131
        Failed to download article: 不返回QuerySets的API from http://www.liujiangblog.com/course/django/131

          • 我试了两次,下面是我两次获取失败的列表
            看着每次都是不一样的失败链接啊

            ”’
            第一次:

            字段查询参数及聚合函数 from Django教程
            URL反向解析和命名空间 from Django教程
            Django内置模板标签 from Django教程
            人类可读性 from Django教程
            第四章:Django表单 from Django教程
            自定制Admin from Django教程
            自定义Admin actions from Django教程
            Admin文档生成器 from Django教程
            网站地图sitemap from Django教程
            3. admin后台 from Django教程
            6. 登录视图 from Django教程
            2.模型设计 from Django教程
            5.Linux下收集数据 from Django教程
            9.前端框架AdminLTE from Django教程
            10.资产总表 from Django教程
            11.资产详细页面 from Django教程

            第二次:
            34% 下载完成
            下载下列文章失败:
            模型和字段 from Django教程
            HttpRequest对象 from Django教程
            配置 Django from Django教程
            Django与CSRF 、AJAX from Django教程
            Django 国际化和本地化 from Django教程
            6.新资产待审批区 from Django教程
            ”’

            • 我测试了一下没有问题。如果是你本地的网络连接这个网站有问题,每次失败的链接不一样是有可能的。

              • 这。。。。
                会不会是引文这个是5线程下载的原因。。
                Synthesizing mastheadImage
                1% 开始下载 [5 线程]…
                既然你能下载,能不能发我一份,我试了n多次,最好成绩3 个fail

                • 之前那个:使用calibre多卷抓取《随园食单》 加子标题的是需要有子标签才可以使用两个forloop 做成return(标题,(子标题,文章))的,但是这个网站的是在一个标签下用了标签 您有没有好的算法?

          • 你好,
            能否帮我解决章节不嵌套的这个问题,不行的话可以先全部下载下来在后期用calibre 搞一下目录。

            我这边网络可能不好,你能不能帮我下一个azw3 profile kindle的django 刘江博客再发给我呢?

            万分感谢

  6. 问一下,我这个代码怎么回事内容都可以下载,标题输出“未知”,21行
    如果是
    title = link.string 输出“未知” 标题
    title = link.contents[0].strip() 输出error
    title = link.a.contents[0].strip() 输出error
    AttributeError: ‘NoneType’ object has no attribute ‘contents’

    python 3.6

    # -*- coding:UTF-8 -*-  
    from calibre.web.feeds.recipes import BasicNewsRecipe
    from urlparse import urljoin
    
    class Downloader(BasicNewsRecipe):
        title = u'Django教程'
        description = u'哈啊 '
        url_prefix = 'http://www.liujiangblog.com'
        no_stylesheets = True
        language = 'zh-CN'
        keep_only_tags = [{ 'class': 'row' }]
        max_articles_per_feed = 5
    
    
        def parse_index(self):
            soup = self.index_to_soup('http://www.liujiangblog.com/course/django/')
            articles = []
            div = soup.find('div', { 'class': 'navbar-sider list-group-item' })
            
            for link in div.findAll('a',{'class':'list-group-item'}):  
                title = link.a.contents[0].strip()
                articles.append({'title': title , 'url':urljoin(self.url_prefix,link["href"])})
            return [(self.title, articles)]
    • 这是因为有的标题 <a> 标签嵌套了 <strong><b> 标签导致的。可以加一个判断,对有子标签的标题额外处理一下:

      # ...
      for link in div.findAll('a', {'class' : ['list-group-item active', 'list-group-item']}):
          if link.find('b') or link.find('strong'):
              title = link.contents[0].contents[0].strip()
          else :
              title = link.contents[0].strip()
      # ...
  7. 能写一个新浪博客的抓取案例吗?比如:http://blog.sina.com.cn/s/articlelist_1664227735_0_1.html。谢谢

    • 这个博客的结构和之前那位小伙伴提供的网站其实是一样的,同样是有翻页的列表,查看一下源代码,找到要保留的区域,然后修改一下代码就行了。还有就是,新浪博客的图片用了懒加载,所以抓取不到。下面是抓取这个博客的完整 Recipe 脚本代码:

      #!/usr/bin/python
      # encoding: utf-8
      
      from calibre.web.feeds.recipes import BasicNewsRecipe
      
      class Zi_Liu_De_Blog(BasicNewsRecipe):
          title = '子柳的博客'
          description = u'子柳的博客'
          __author__ = '子柳'
          language = 'zh'
          encoding = 'utf-8'
      
          content_regex = '^.*articalContent.*$'
          keep_only_tags = [
              dict(name='div', attrs={'class':'articalTitle'}),
              dict(name='div', attrs={'class':'sina_keyword_ad_area2'}),
              dict(name='div', attrs={'class':re.compile(content_regex, re.IGNORECASE)})
          ]
          # time_regex = '^.*time.*$'
          # remove_tags = [dict(name='span', attrs={'class':re.compile(time_regex, re.IGNORECASE)})]
          no_stylesheets = True
          remove_javascript = True
          auto_cleanup = True
          auto_cleanup_keep = '//div[@class="articalTitle"]'
          max_articles_per_feed = 999
      
          def parse_index(self):
              site = 'http://blog.sina.com.cn'
              page = site +'/s/articlelist_1664227735_0_'
              start = 1
              end = 2
              articles = []
              for p in range(start, end + 1) :
                  soup = self.index_to_soup(page + str(p) + '.html')
                  links = soup.findAll("span",{"class":"atc_title"})
                  for link in links:
                      title = link.a.contents[0].strip()
                      url = link.a.get("href")
                      a = {'title': title , 'url':url}
                      articles.append(a)
              ans = [(self.title, articles)]
              return ans
  8. 在centos下运行“ebook-convert wangyin.recipe wangyin.mobi –output-profile kindle”,报错:

    /home/dev/tmp>ebook-convert ‘wangyin.recipe’ ‘wangyin.mobi’ –output-profile kindle
    Conversion options changed from defaults:
    test: None
    output_profile: u’kindle’
    1% Converting input to HTML…
    InputFormatPlugin: Recipe Input running
    Using custom recipe
    1% Fetching feeds…
    1% Got feeds from index page
    1% Trying to download cover…
    1% Generating masthead…
    Synthesizing mastheadImage
    This application failed to start because it could not find or load the Qt platform plugin “headless”
    in “/opt/calibre/lib/python2.7/site-packages/calibre/plugins”.

    Available platform plugins are: headless (from /opt/calibre/lib/python2.7/site-packages/calibre/plugins), linuxfb, minimal, offscreen, xcb.

    Reinstalling the application may fix this problem.
    Aborted

    请问这个是什么问题?怎么解决?谢谢

  9. 把site行跟links行改过之后套用这个recipe运行还是会提示下载失败文章,查看运行日志里错误信息是【Exception: 无法获取文章。 调试用信息出现在日志文件前部】
    我抓取的站点是https://www.merriam-webster.com/words-at-play/see-all,links行soup.findAll内写的是”h5″,{“class”:”typo10″},请问还有哪里需要修改的吗?

    • 这个站点需要对文中的代码做一下修改,因为列表页不是网站首页。把 site、soup、links 这三个变量替换成下面这四个变量就可以了:

      site = 'https://www.merriam-webster.com' # 网站地址
      page = site + '/words-at-play/see-all' # 页面列表页
      soup = self.index_to_soup(page) # 解析列表页返回 BeautifulSoup 对象
      links = soup.findAll("h5",{"class":"typo10"}) # 获取所有文章链接

      另外,这样代码也只能抓取一个页面的链接,这个文章列表是有分页的,想要抓取更多链接还需要处理分页。处理分页,把代码中的 parse_index() 函数代码块替换成如下代码块即可。注意代码中的页面范围设定,变量 start 是开始页码,变量 end 是结束页码。

      def parse_index(self):
          site = 'https://www.merriam-webster.com' # 网站地址
          page = site +'/words-at-play/see-all' # 页面列表页
          start = 1 # 开始页码
          end = 3 # 结束页码
          articles = [] # 定义空文章资源数组
          for p in range(start, end + 1) :
              soup = self.index_to_soup(page + '?p=' + str(p)) # 解析列表页返回 BeautifulSoup 对象
              links = soup.findAll("h5",{"class":"typo10"}) # 获取所有文章链接
              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 转换的数据结构
      • 十分感谢!!!我按照参考资料中抓取乌云知识库那一篇文也改过一下自己的语句,但也还是一直抓取失败,今天再看到回复突然意识到?p=1时url失效导致抓取失败,改过之后果然成功了xD