Gerando periódicos para kindle a partir de RSS ou Atom feeds

Alguns meses atrás eu comprei um kindle paperwhite pela internet. Como a maioria dos usuários eu utilizo o Calibre para gerenciar minha biblioteca e também para fazer download notícias distribuídas em rss ou atom. No caso dos rss o Calibre gera o formato “periodical”, no qual você navega pelos feeds clicando em tabs, como mostrado na figura abaixo:

IMG_20140820_131010

Pensei, seria possível implementar algum script simples para simplesmente baixar os feeds e gerar um arquivo .mobi sem precisar de algum programa muito elaborado, como o Calibre pra isso? A reposta é sim, e é bem mais fácil do que pensei, se contarmos com o auxílio de algumas libs do python.

Basicamente, o que precisamos fazer é baixar cada notícia distribuída por rss ou atom, gerar um epub e utilizar o aplicativo kindlegen da Amazon para gerar o arquivo .mobi. Também não devemos esquecer de processar as imagens, ou seja, temos que fazer download das mesmas e referenciá-las localmente em cada artigo e colocá-las dentro do epub.

Primeiro, baixamos os feeds com o feedparser, com ele dada a url do feed podemos processar cada notícia, como mostrado no código abaixo exemplificando como  obtemos cada notícia (entries), imprimindo o título e o conteúdo.

import feedparser

feed = feedparser.parse('http://feeds.feedburner.com/JavaCodeGeeks')
for entry in feed.entries:
   print entry.title
   print entry.content[0]['value']

Em seguida, temos a parte mais trabalhosa: a geração do arquivo epub, que claro, segue um formato pré-determinado. Um arquivo epub é um zip (ver aqui) contendo alguns arquivos-padrão que descrevem o conteúdo e definem a navegação dentro do arquivo, seguindo a seguinte estrutura:

arquivo.epub/
META-INF/container.xml
content.opf
content.html
coverpage.xhtml
mimetype
toc.ncx

content.xml: é o arquivo que define o ponto de entrada da publicação, no nosso caso ele aponta para o arquivo “content.opf”
content.opf: define todos os arquivos que fazem parte da publicação, incluindo xhtml, css, imagens, opf, ncx, etc. Abaixo segue um exemplo de um content.opf para um epub com um único artigo contendo apenas uma imagem.

<?xml version='1.0' encoding='utf-8'?>
<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="uuid_id" version="2.0">
  <metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dcterms="http://purl.org/dc/terms/"
   xmlns:dc="http://purl.org/dc/elements/1.1/">

    <dc:publisher>rss2kindle.py</dc:publisher>
    <dc:description> Generated by rss2kindle.py </dc:description>
    <dc:language>pt_BR</dc:language>
    <dc:creator opf:file-as="rss2kindle.py" opf:role="aut">rss2kindle.py</dc:creator>
    <dc:title>rss2kindle.py</dc:title>
    <meta name="cover" content="cover"/>
    <dc:date> 31/7/2014 </dc:date>
    <dc:contributor opf:role="bkp">rss2kindle.py</dc:contributor>
    <dc:identifier id="uuid_id" opf:scheme="uuid">72ebf8f1-9a52-4b8a-8013-cd5f54166b2a</dc:identifier>
    <meta name="rss2kindle:publication_type" content="periodical:unknown:Notícias"/>

     <x-metadata>
      <output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/>
     </x-metadata>

    </metadata>

    <item href="cover.jpg" id="cover" media-type="image/jpeg"/>

    <manifest>
       <item href="coverpage.xhtml" id="coverpage" media-type="application/xhtml+xml"/>
       <item href="toc.ncx" id="tocncx" media-type="application/x-dtbncx+xml"/>
       <item href="contents.html" id="contents" media-type="application/xhtml+xml"/>

       <item href="article_0.html" id="id0" media-type="application/xhtml+xml"/>
       <item href="images/Weekend_Nerd_image_0.jpeg" id="id20" media-type="image/jpeg"/>
     </manifest>

     <spine toc="tocncx">
       <itemref idref="coverpage"/>
       <itemref idref="contents"/>
       <itemref idref="id0"/>
     </spine>

     <guide>
        <reference href="contents.html" type="toc" title="Table of Contents"/>
     </guide>

</package>

Nas linhas 19 a 20 temos as tags que devemos adicionar no content.opf para fazer com que nosso epub seja visualizado com tabs e não como um ebook normal, como mostrado na figura anterior. Mais detalhes veja a thread sobre esse assunto na stackoverflow.

Dentro da seção manifest definimos os arquivos que farão parte do epub, incluindo imagens. No código acima definimos uma página de rosto (coverpage.html), o índice (toc.ncx) ,content.html e os artigos e figuras que iremos exibir, cada um id definido que, posteriormente será usado na seção spine, onde é definida uma ordem linear de exibição do documento. Mais detalhes sobre o content.opf veja nesse link.

toc.ncx: define a hierarquia e também a navegação entre os capítulos, seções e subseções de um epub. Abaixo o exemplo de conteúdo desse arquivo para apenas um artigo. Repare que dentro de navMap definimos a hierarquia do nosso epub dividido em seções, que no nosso caso correspondem aos feeds que queremos ler, cada um correspondendo a um navPoint, com os respectivos artigos aninhados, também por um navPoint.

<?xml version='1.0' encoding='utf-8'?>
<ncx xmlns:mbp="http://mobipocket.com/ns/mbp" xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="eng">
  <head>
    <meta content="45112bf4-0a26-406d-8207-148c3350e36c" name="dtb:uid"/>
    <meta content="3" name="dtb:depth"/>
    <meta content="rss2kindle.py" name="dtb:generator"/>
    <meta content="0" name="dtb:totalPageCount"/>
    <meta content="0" name="dtb:maxPageNumber"/>
  </head>
  <docTitle>
    <text>RSS Feeds Generated by python</text>
  </docTitle>

  <navMap>
    <navPoint playOrder="0" class="periodical" id="periodical">
    <navLabel>
      <text>Table of Contents</text>
    </navLabel>
    <content src="contents.html"/>

    <navPoint class="section" id="num_1" playOrder="1">
      <navLabel>
        <text>Weekend Nerd</text>
      </navLabel>
     <content src="article_0.html"/>

       <navPoint class="article" id="num_2" playOrder="2">
         <navLabel>
             <text>Python é lento? Que Python?</text>
         </navLabel>
         <content src="article_0.html"/>
         <mbp:meta name="description"> Originally posted on a href="http://pythonhelp.wordpress.com/2013/11/19/python-e-lento-que-python"</mbp:meta>
         <mbp:meta name="author">Eduardo</mbp:meta>
       </navPoint>
      </navPoint>
   </navPoint>
  </navMap>
</ncx>

content.html: não é um arquivo obrigatório do epub, mas eu prefiro criar e referenciá-lo no primeiro navPoint, contendo o índice do ebook. Abaixo temos o conteúdo dele, onde o nome de cada feed aparece na tag h4 seguido de uma lista de artigos baixados em cada feed, nas tags ul e li. Repare que cada li aponta para o arquivo html do artigo. No kindle quando você visualiza o content.html, basta clicar no em um dos links abaixo para ir direito ao artigo que você quer ler.

<html>
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
    <title>Table of Contents</title>
  </head>
  <body>
    <h1>rss2kindle.py</h1>
    <h4>Weekend Nerd</h4>
      <ul>
          <li><a href='article_0.html'>Python é lento? Que Python?</a></li>
      </ul>
  </body>
</html>

coverpage.xhtml: arquivo onde você define uma capa ou folha de rosto do epub. No nosso caso ele contém apenas uma figura para ser exibida no kindle quando visualizarmos nossos ebooks no modo “cover view”, como mostrado no código e na figura abaixo:

 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <meta name="cover" content="true"/>
        <title>Cover</title>
        <style type="text/css" title="override_css">
            @page {padding: 0pt; margin:0pt}
            body { text-align: center; padding:0pt; margin: 0pt; }
        </style>
         <link rel="stylesheet" type="text/css" >
    </head>
    <body>

      <h1>rss2kindle.py</h1>
      <h2> 31/7/2014 </h2>

        <div>
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
            width="100%" height="100%" viewBox="0 0 590 750" preserveAspectRatio="none">
                <image width="590" height="750" xlink:href="cover.jpg"/>
            </svg>
        </div>
    </body>
</html>

IMG_20140820_130935

mimetype: arquivo contendo o tipo de publicação. No nosso caso ele sempre terá uma linha contendo: “application/epub+zip”

Agora que conhecemos como é um epub por dentro podemos fazer um programa que gere o conteúdo dos arquivos mostrados acima da seguinte maneira:

1) Cada feed corresponderá a um navPoint da classe section em toc.ncx e cada artigo do feed corresponderá a um navPoint da classe ‘article’ aninhado ao primeiro.

2) Cada artigo baixado e suas imagens terão um item correspondente em content.opf, na seções manifest e spine.

3) Em content.html, para cada feed criamos um heading h4 colocando o nome dele seguido de um lista ul contendo o link para cada artigo.

4) empacotamos todos os arquivos em zip e renomeamos para .epub

5) Com o kindlegen geramos o .mobi

No caso das imagens eu utilizei uma lib específica para manipular HTML’s, a Beautiful Soap. Tentei aglumas outras que já vem com o python, mas o resultado não era muito legal, até que pesquisando na internet achei um blog que apresenta o uso dela justamente na geração de um epub com figuras.

Uma coisa que achei fantástico em python foi a facilidade de geração dos arquivos toc.ncx e content.opf devido ao suporte que a linguagem oferece para manipular strings de múltiplas linhas sem ter que utilizar uma lib externa para isso. Basta substituir, na string, variáveis identificados com “%(nome_variavel)tipo da variável” passando um mapa contendo os valores, como se fosse uma template.

O código abaixo mostra como gerar o navPoint no toc.ncx para cada artigo do feed.

article_navpoint_tpl = """
      <navPoint class="article" id="num_%(order)s" playOrder="%(order)s">
          <navLabel>
            <text>%(article_title)s</text>
          </navLabel>
          <content src="%(article_link)s"/>
          <mbp:meta name="description"> %(description)s </mbp:meta>
          <mbp:meta name="author">%(author)s</mbp:meta>
        </navPoint>
"""

article_navpoint_tpl % {"article_title":entry.title, \
                        "article_link": "article_{0}.html".format(article_counter),\
                        "order": play_order,\
                        "description": description,\
                        "author":entry.author}

O código completo do script python para gerar o epub você pode encontrar aqui e, o link pra o meu pequeno projeto de gerador de periódicos para kindle, o rss2kindle.py, no github está aqui. Agora não preciso mais ficar instalando e configurando algum aplicativo ou me inscrever em sites que fazem a envio de artigos para o kindle através da internet, para poder ler os meus feeds favoritos.

Abaixo dois arquivos gerados pelo rss2kindle:

rss2kindle_2014820.epub

rss2kindle_2014820.mobi

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: