PlanetaSIG com extractos de posts com 500 caracteres

Tempo de leitura: 4 min

A pedido de um dos autores, a partir de hoje, o PlanetaSIG tem a capacidade de mostrar apenas os primeiros N caracteres de cada post. A configuração proposta é mostrar 500 caracteres, permitindo aos leitores do agregador decidir se lhe interessa visitar o blog original para ler o post completo.

O agregador tem funcionado inspirado no PlanetGS e como tal mostra numa única página os posts de forma integral, dos blogs que agrega.

Pessoalmente, leio o PlanetaSIG bem como outros planetas e blogs com o Google Desktop, usando a sidebar onde vejo os títulos dos itens a serem refrescados. Ao clicar num item, é aberto directamente o blog original, sem passar pelo PlanetaSIG (ou outro agregador).

Acontece que o PlanetaSIG pode ser consultado directamente, visitando a página. E nesse caso é bem possível que esse visitante já não irá visitar os blogs originais, reduzindo o tráfego desses blogs. E compreendo perfeitamente os autores que querem evitar esta situação (e mesmo que não concordasse agiria da mesma forma – o autor é soberano).

Com esta nova possibilidade espero resolver a questão de forma simpática para todos.

Portanto, quem tem o seu blog agregado no PlanetaSIG e que assim deseje pode enviar-me um email e eu configurarei o seu feed para mostrar apenas um extracto dos posts. O modo default para quem não se manifestar continuará a ser mostrar os posts integrais, mantendo um pouco a mesma lógica dos planetas mais globais como o PlanetGS e o PlanetOSGeo.

Aproveito para pedir sugestões para blogs que possam ser incluídos no PlanetaSIG!!

Detalhes técnicos

O PlanetaSIG é gerado pelo software Venus, escrito em Python. O Venus não permite de raíz configurar o n.º de caracteres a mostrar em cada post, nem permite usar o sumário ou excerpto incluído nos feeds, em vez de mostrar o conteúdo. Já antes tinha tentado configurar o Venus para fazer esse efeito mas sem sucesso.

Mas o Venus tem a capacidade de aplicar filtros a cada feed RSS, e de forma independente. Ou seja, podemos aplicar um filtro a um feed, e outro filtro diferente noutro feed, e até ter outros feeds sem filtro algum.

Um filtro é um pequeno script escrito em Python (também pode ser um xslt), que vai ser executado para cada item dentro de um feed, podendo transformá-lo da maneira como o programador quiser. Por exemplo, podem retirar-se todas as referências a imagens, ou substituir tags <h1> por <h3> ou outro qualquer, aplicar classes css a determinados tags, etc.

O que acabei por fazer foi criar um filtro que pega no <content> de cada post e o substitui por apenas os primeiros N caracteres do original. Este filtro pode ainda substituir o <content> por outro tag qualquer – por exemplo, copiar o sumário para o content. Como o Venus só consegue mostrar o <content>, passa a mostrar o sumário sem saber…

O código do novo filtro foi baseado num filtro que vem já incluído no Venus – excerpt.py. Fica aqui o código para referência futura.

Até breve.

Cria um novo elemento ou substitui um existente,
com o texto de outro elemento, truncado com X caracteres.
Baseado no filtro excerpt.py e alterado por Duarte Carreira em 16/Julho/2009.

Parameters:
  width:  maximum number of characters in the excerpt.  Default: 500
  omit:   whitespace delimited list of html tags to remove.  Default: none
  target: name of element created.  Default: content
  source: name of element to get data from. Default: summary
  replace: yes to delete duplicate target. Default: yes

Example to test:
python tests/reconstitute.py http://localhost/feedorig.xml
--filters "planetaSIG.py?width=500&source=content&target=content&replace=yes">tes
te3.xml

Notes:
* if you want to avoid duplicate entries use replace=yes.
* Venus does not expose summary in the feeds to tmpl templates. With this filter,
   you cant replace the text inside content with the text from summary. This is
   what the default values do.
* if 'img' is in the list of tags to be omitted <img> tags are replaced with
   hypertext links associated with the value of the 'alt' attribute.  If there
   is no alt attribute value, <img> is used instead.  If the parent element
   of the img tag is already an <a> tag, no additional hypertext links are
   added.
"""

import sys, xml.dom.minidom, textwrap
from xml.dom import Node, minidom

atomNS = 'http://www.w3.org/2005/Atom'
planetNS = 'http://planet.intertwingly.net/'

args = dict(zip([name.lstrip('-') for name in sys.argv[1::2]], sys.argv[2::2]))

wrapper = textwrap.TextWrapper(width=int(args.get('width','500')))
omit = args.get('omit', '').split()
target = args.get('target', 'content')
original = args.get('source', 'summary')
replace = args.get('replace','yes')

class copy:
    """ recursively copy a source to a target, up to a given width """

    def __init__(self, dom, source, target):
        self.dom = dom
        self.full = False
        self.text = []
        self.textlen = 0
        self.copyChildren(source, target)

    def copyChildren(self, source, target):
        """ copy child nodes of a source to the target """
        for child in source.childNodes:
            if child.nodeType == Node.ELEMENT_NODE:
                 self.copyElement(child, target)
            elif child.nodeType == Node.TEXT_NODE:
                 self.copyText(child.data, target)
            if self.full: break

    def copyElement(self, source, target):
        """ copy source element to the target """

        # check the omit list
        if source.nodeName in omit:
            if source.nodeName == 'img':
               return self.elideImage(source, target)
            return self.copyChildren(source, target)

        # copy element, attributes, and children
        child = self.dom.createElementNS(source.namespaceURI, source.nodeName)
        target.appendChild(child)
        for i in range(0, source.attributes.length):
            attr = source.attributes.item(i)
            child.setAttributeNS(attr.namespaceURI, attr.name, attr.value)
        self.copyChildren(source, child)

    def elideImage(self, source, target):
        """ copy an elided form of the image element to the target """
        alt = source.getAttribute('alt') or '<img>'
        src = source.getAttribute('src')

        if target.nodeName == 'a' or not src:
            self.copyText(alt, target)
        else:
            child = self.dom.createElement('a')
            child.setAttribute('href', src)
            self.copyText(alt, child)
            target.appendChild(child)

    def copyText(self, source, target):
        """ copy text to the target, until the point where it would wrap """
        if not source.isspace() and source.strip():
            self.text.append(source.strip())
        lines = wrapper.wrap(' '.join(self.text))
        if len(lines) == 1:
            target.appendChild(self.dom.createTextNode(source))
            self.textlen = len(lines[0])
        elif lines:
            excerpt = source[:len(lines[0])-self.textlen] + u' \u2026'
            target.appendChild(dom.createTextNode(excerpt))
            self.full = True

# select summary or content element
dom = minidom.parse(sys.stdin)

#source = dom.getElementsByTagNameNS(atomNS, 'summary')
#if not source:
#    source = dom.getElementsByTagNameNS(atomNS, 'content')
source = dom.getElementsByTagNameNS(atomNS, original)

# if present, recursively copy it to a planet:excerpt element
if source:
    fonteelem = source[0]
    if target.startswith('planet:'):
        dom.documentElement.setAttribute('xmlns:planet', planetNS)
    if target.startswith('atom:'): target = target.split(':',1)[1]
    excerpt = dom.createElementNS(planetNS, target)
    source[0].parentNode.appendChild(excerpt)
    copy(dom, source[0], excerpt)
    #source[0].parentNode.replaceChild(excerpt, source[0])
    #if source[0].nodeName == excerpt.nodeName:
    #  source[0].parentNode.removeChild(source[0])

#apagar o original
if replace == 'yes':
    source = dom.getElementsByTagName(target)
    fonteelem = source[0]
    if len(source)>1:
        source[0].parentNode.removeChild(source[0])

# print out results
print dom.toxml('utf-8')

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *