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

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

给这篇文章写一条留言

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

小伙伴们写下了 72 条留言

  1. 你好,想请教下,某些章节(article)会下载失败,在浏览器上单独打开该链接是正常的,重新生成电子书该章节也正常,但其它章节可能又失败了,有没有办法对章节下载设置失败重试次数?单独把timeout 改大没有用。

    from calibre.web.feeds.recipes import BasicNewsRecipe
    
    class Git_Pocket_Guide(BasicNewsRecipe):
    
        url_base = 'https://m.bookabc.cc'
        url_home = 'https://m.bookabc.cc/abc/117/117754/'
        index_list = ['index_1.html', 'index_2.html','index_3.html','index_4.html','index_5.html','index_6.html','index_7.html','index_8.html', 'index_9.html'] ##
    
        title = '冰河末世,我囤积了百亿物资'
        description = '末世+重生+爆囤物资+苟+无限空间+黑化复仇不圣母 '
    
        keep_only_tags = [{ 'class': 'chapter' }, {'class':'nr_nr'}]  ## 仅保留这些元素
        no_stylesheets = True ## 去除样式
        delay = 0.1  ## 下载间隔,秒
        simultaneous_downloads = 1  ## 一个进程下载
        max_articles_per_feed = 20000000  ## 不限制下载章节
        timeout = 300  ## 超时时间,秒
        ## ignore_duplicate_articles = {'title', 'url'}  ## 删除重复
    
        def get_cover_url(self):
            ## cover_url = 'https://m.bookabc.cc/img/117/117754.jpg'  ## 封面
            soup = self.index_to_soup(self.url_home)
            div = soup.find('div', { 'class': 'block_img2' })
            img = div.find('img')
            return self.url_base + img['src']
    
        def parse_index(self):
            articles = []
            for index_str in self.index_list:
                soup = self.index_to_soup(self.url_home + index_str)
                for ul in soup.find_all('ul'):
                    for link in ul.find_all('a'):
                        if not 'abc' in link['href']:
                            continue
                        til = link.get_text()
                        url =  self.url_base + link['href']
                        a = { 'title': til, 'url': url }
                        articles.append(a)
                        for i in range(2,4,1):
                            til_tmp = til + '_' + str(i)
                            url_tmp = url.replace('.html', '_' + str(i) + ".html")
                            articles.append({ 'title': til_tmp, 'url': url_tmp })
                        ## print('add:' + til)
            ans = [('book', articles)]
            return ans
    • 好像没有相关的设置。不过你可以通过覆写相关的方法来控制请求的重试:

      def retry_fetch(self, func, *args):
          limit = 5  # 重试次数
          count = 0
          while True:
              try:
                  return getattr(BasicNewsRecipe, func)(self, *args)
              except Exception as e:
                  if count < limit:
                      count += 1
                      url = args[0] if len(args) else self.cover_url
                      self.log.warn('[%s] Retry to fetch link: %s' % (count, url))
                      continue
                  raise e
      
      def download_cover(self):
          return self.retry_fetch('download_cover')
      
      def index_to_soup(self, url_or_raw, raw=False, as_tree=False):
          return self.retry_fetch('index_to_soup', url_or_raw, raw, as_tree)
      
      def fetch_article(self, url, dir, f, a, num_of_feeds):
          return self.retry_fetch('fetch_article', url, dir, f, a, num_of_feeds)
  2. 抓取新闻以后的书名title怎么样设置为书名[星期,日 月 年]这样的格式?之前3.2版本是自动可以搞出来的,到了5.81就不知道怎么设置了。😂研究了半天都没看到哪里有设置的……

    • 可以查看 Calibre 的 news.py 源代码,参考 487 行的 get_browser() 函数,注释提供了示例用法,你可以参考它在你自己的脚本里重写这个函数以实现登录功能。

  3. 请问https://chuansongme.com/account/m15875 这里的文章怎么采集,老是没有成功,谢谢。

    • 即便 Calibre 升级到 Python3,对 Recipe 脚本的影响应该不会很大,如果有必要就会更新。

      我曾试过用 4.x 版本抓取网站内容,感觉并没有旧版本效果好,比如有时候,程序会认为页面上有些图片格式不符合标准,强行忽略,而 3.x 就比较宽容。

  4. 抓取这个网页
    https://www.gamersky.com/handbook/201512/692422_181.shtml
    只有文字没有图片
    我写的代码如下

    from calibre.web.feeds.news import BasicNewsRecipe
    
    class AdvancedUserRecipe1578841231(BasicNewsRecipe):
        title = '《侠客风云传》开局及全流程指导图文攻略' # 电子书名
        #cover_url = '' # 电子书封面
        #masthead_url = '' # 页头图片
        __author__ = '写小说的立青'
        language = 'zh' # 语言
        encoding = 'utf-8' # 编码
    
        # 抓取页面内容设置
        keep_only_tags = [{'name':'div', 'class': 'Mid2L_con'}]
        remove_tags = [{'name':'div', 'class':'gs_ccs_solve'},
                        {'name':'span', 'class':'pagecss'}]
        #no_stylesheets = True # 去除 CSS 样式
        #remove_javascript = True # 去除 JavaScript 脚本
        #auto_cleanup = True # 自动清理 HTML 代码
        delay = 3 # 抓取页面间隔秒数
        max_articles_per_feed = 999 # 抓取文章数量
        
        # 页面内容解析方法
        def parse_index(self):
            site = 'https://www.gamersky.com/handbook/201512/692422'
            articles = [] # 定义空文章资源数组
            articles.append({'title':'第1页', 'url':site+'.shtml'})
            for i in range(2, 182):
                articles.append({'title':'第%d页' % i, 'url':site+'_'+str(i)+'.shtml'})
                
            ans = [(self.title, articles)] # 组成最终的数据结构
            return ans # 返回可供 Calibre 转换的数据结构
    • 页面使用了图片 Lazy Load 技术,你需要对其做处理。以你提供的 Recipe 为例,只需要重写 preprocess_raw_html() 函数即可:

      def preprocess_raw_html(self, raw_html, url):
          soup = BeautifulSoup(raw_html, 'lxml')
          for img in soup.find_all('img', class_='picact'):
              img['src'] = img['data-src']
          return unicode(soup)
  5. 哇,想不到这么老的帖子博主还在回答。请教一个让我快头烂的问题:

    我写了一个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,以获取更详尽的日志信息,看有没有和抓取图片相关的信息。

  6. 您好!
    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,在循环时根据制定的那个范围来判断文章从属哪个章节:

      #!/usr/bin/env python
      # -*- coding:utf-8 -*-
      
      from urlparse import urljoin
      from bs4 import BeautifulSoup
      from calibre.web.feeds.recipes import BasicNewsRecipe
      
      class Zangyao(BasicNewsRecipe):
          title = u'藏要'
          __author__ = u'欧阳竞无'
          language = 'zh'
      
          siteurl = 'http://www.guoxue123.com/zhibu/0101/04zy/index.htm'
          feeds = [
              (u'第一輯(十一經三律十一論)', 0, 221),
              (u'第二輯(八經六律十三論)', 222, 388),
              (u'第三輯(十經二律九論)', 389, 517),
          ]
      
          keep_only_tags = [
              dict(name='table', border='1'),
          ]
      
          def parse_index(self):
              urls = []
              result  = self.index_to_soup(self.siteurl, raw=True)
              soup = BeautifulSoup(result, 'html5lib')
      
              items = soup.select('td[width="87%"] a')
              for feed in self.feeds:
                  topic = feed[0]
                  # 根据URL中的序号设定当前章节的范围
                  scope = range(feed[1], feed[2])
                  articles = []
                  for item in items:
                      title = item.get_text().strip()
                      href = item.get('href')
                      link = urljoin(self.siteurl, href)
                      # 提取URL中的序号,如果不在范围内则忽略
                      number = int(href.strip('.html'))
                      if number not in scope:
                          continue
                      articles.append({
                          'title' : title,
                          'url': link,
                          'author': self.__author__
                      })
                  if articles:
                      urls.append((topic, articles))
              return urls

      不过这个脚本只是把页面上的内容简单抓取下来了,排版效果不怎么好,如果想近一步优化排版,就需要学学 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

  7. 请问有没有办法把calibre抓的Taipei Times上的图片放大,使图片能够填满屏幕,就像经济学人上的图片似的,可能是由于图片像素太小吧!放到kindle上看图片总是缩到左边。

  8. 每章后面都有的一句话,有没有办法删掉呢
    This article was downloaded by calibre from xx

    • 解决方法就是在转换时添加参数 --output-profile,建议仔细阅读本文第四部分内容,已有说明。