Suite inforporn avec RSS

09/10/04

J'aime bien être au courant. Je lis beaucoup de newsgroups sur Usenet mais ce n'est pas assez. Je m'inscris à des mailing lists mais j'ais peur d'augmenter encore ma charge de spam. J'avais envie de commencer à explorer les arcanes de l'univers XML. Je me suis mis au RSS.

Introduction

RSS signie selon les gens à qui on demande Really Simple Syndication ou Rich Site Summary. Quoi qu'on choisisse, c'est tout bon pour nous. La mécanique est simple : le serveur web a un fichier RSS que l'on nomme aussi feed/fil, codé la plupart du temps en XML, qui contient un résumé riche du site, c'est à dire une liste des liens récement mis à jour avec leurs descriptions et diverses informations ou plus généralement une liste d'informations. Si vous avez ce que l'on appelle un aggrégateur de fils RSS, il va récupérer pour vous les fichiers selon une périodicité définie et, si tout se passe sans encombres, vous recevez une sorte de message de notification du nouveau contenu du site sans avoir à le visiter en personne.

De nombreux sites tels que Drobe launchpad, BBC News, Libération ou SlashDot proposent de tels fils RSS. On trouve de tout en format RSS : des actualités, des blogs, des notifications de mises à jour logicielles, ... etc.

Aggrégation

L'aggrégation est le processus par lequel on réunit les informations fournies par les fils RSS. Il existe des sites qui font cette agrégation tout seuls selon des sujets préçis (Google actualités, Meerkat) et on parle alors de syndication, des sites qui font de l'agrégation selon vos désirs (l'ancien My Netscape ou le mode personnel de Meerkat) ou bien des logiciels dédiés, les aggrégateurs. Nous allons nous concentrer sur la partie logicielle car pourquoi faire faire quelque chose alors que l'on peut trés bien le faire soi-même ? Et puis si nous générons des fils nous même, alors cela évite d'avoir à les mettre en ligne pour qu'ils soient aggrégés comme nous allons le voir.

Il existe de trés nombreux aggrégateurs logiciels, tellement que je ne saurais vous en conseiller un, mais vous trouverez ici ou ici une liste des plus populaires. Trés franchement aucun ne m'a réellement convaincu, peut-être car je suis très exigeant en terme d'interface graphique. Alors que je testais différents clients IMAP clients pour un autre projet, je suis tombé jaloux de la manière dont Mozilla ThunderBird gère les fils RSS à l'intérieur de l'interface classique de messagerie. C'est amusant car un des arguments employés par de nombreux logiciels est justement que leur aggrégateur ressemble à un logiciel de messagerie.

Après avoir désespérément cherché un bon aggrégateur RSS pour mon Iyonix, je me suis dit qu'il y avait bien quelque chose à faire. J'ai trouvé NewsPipe par Ricardo M. Reyes qui fournit le service que je recherchais. C'est un petit programe en Python qui envoie des e-mails à une adresse choisie quand un site a été mis à jour, c'est à dire quand le fil RSS a été récupéré et qu'il montre de nouvelles choses. Une sorte de passerelle RSS vers e-mail. J'ai bien essayé de le faire marcher avec le Python pour RISC OS mais je n'y suis pas parvenu donc j'ai décidé de l'installer sur le pc, ce qui n'est pas gênant dans la mesure où les deux machines sont de toutes façons forçées de coopérer. Python existe sous de nombreuses platterforme donc vous ne devriez pas avoir de difficultés.

J'ai commencé par installer Python puis j'ai mis NewsPipe sur le disque dur de Piotr, je l'ai configuré pour envoyer des messages en format texte brut directement à MessengerPro sur l'Iyonix, puis j'ai créé mon fichier OPML pour qu'il se connecte dans des mesures temporelles acceptables à des sites que je visite fréquement. Boîtes créées dans MessengerPro, règles de filtrage adéquates et j'avais mon système qui simulait le fonctionnement de Mozilla ThunderBird. Vous aussi, avec votre logiciel de messagerie préféré, retrouvez toute la puissance de l'aggrégation RSS sans avoir à installer un nouveau logiciel et surtout en ayant une interface unifiée.

Trouver des fils

Quand vous visitez un site fréquement, guettez des petits logos du type rss, xml ou toute mention de fil/feed RSS, XML, syndication ou terme analogue. Copiez alors le lien vers lequel ces fichiers pointent et vous avez l'URL en question. Ajouter la à votre fichier OPML et c'est bon.

Même si la technologie RSS n'a pas encore son Google à proprement parler, il existe des sites de référence tels que Syndic8.com.

Génération de fils

Si un de vos sites préférés ne dispose pas encore de fil RSS alors créez le ! Les fils RSS existent sous différents formats en raison d'une histoire chaotique et de multiples guerres de protocoles. Nous n'allons pas entrer ici dans la description et l'analyse des formats mais sachez que personnellement, j'utilise 1.0 car il permet de faire beaucoup de choses tout en restant suffisament simple pour que l'on puisse le générer à la main comme on le fait avec du HTML.

Tous les scripts que nous allons voir sont en perl et utilisent le module XML::RSS::SimpleGen qui est le plus simple mais produit du 2.0. Il y a bien le bon vieux XML::RSS avec lequel on peut choisir le type de fil RSS que l'on veut générer mais il est plus compliqué d'emploi.

Il faut éxécuter ces scripts toutes les 24 heures en général grâce au planificateur de tâches ou à un cron tab.

Si vous générez des fils sur un site internet et non en mode local, n'oubliez pas de configurer votre serveur (fichier .htaccess pour Apache) pour qu'il renvoie le type mime correct, à savoir application/xml+rss rss.

Premier pseudo fil

Passons à la pratique et générons notre premier fil. Cela va être très simple au point que ce n'est qu'un pseudo fil. Imaginons qu'une de vos amies a un site internet et qu'elle ne le met à jour que trés rarement. C'est ennuyeux d'avoir à le visiter pour voir si quelque chose a bougé et c'est énervant de rater une mise à jour. Il existe bien d'autres moyens de faire cela et cet example n'est pas intéressant en soi mais c'est juste pour mettre en place les concepts.

#!perl -w
# Lemon watcher

use LWP::Simple;
use XML::RSS::SimpleGen;

$url = "http://www.lemonbugg.com/poo.html";
# récupération des headers http
@headz = head($url);

# cas où le site a été modifié pendant les dernières 24 heures
if ((time - $headz[2]) > 24*60*60) {
  # Ecriture du fil
  rss_new ("http://www.lemonbugg.com","Lemon","Her updates");
  rss_item ($url,"Something new about Lemon on ".time);
  rss_save('lemon.rss');
}
exit(0);

Comme vous le voyez c'est très simple. On va récupérer les headers de la page HTML en question pour savoir si une modification est intervenue dans les dernières 24 heures. Si tel est le cas, on génère un pseudo fil RSS avec juste une seule entrée qui contient la date unix du moment où cette mise à jour a été remarquée. Il ne vous reste plus qu'à éxécuter ce script automatiquement toutes les 24 heures, ajouter à votre fichier OPML, <outline text="Lemon" htmlUrl="http://www.lemonbugg.com/" xmlUrl="f:\tests\lemon.rss" delay="60" />, et vous serez notifié dès que le site bouge. Plus besoin de signets dynamiques, les nouvelles viennent à vous. Notez au passage qu'en terme de bande passante, le procesus est très économique car nous n'avons pris que les en-têtes alors qu'une visite impliquerai toute la page et éventuellement sa feuille de style et ses images. Belle économie moyens et de ressources.

Un fil simple

Notre deuxième example me permet de surveiller le site du FC Barcelone car je suis un grand fan du Barça mais n'ai pas la patience d'attendre. C'est un site où on trouve des liens vers de petits articles et nous allons transformer la partie de cette page concernant les actualités en fil RSS.

#!perl -w
# Barca headlines RSS generator

use LWP;
use XML::RSS::SimpleGen;

$base = "http://www.fcbarcelona.com";

$browser = LWP::UserAgent->new;
$answer = $browser->get("$base/eng/home-page/home/home.shtml");
$html = $answer->content;
@content = ($html =~ /href=(\/eng\/noticias\/noticias\/n\d+\.\w+) class="b10b?">([^>]+)/g);

rss_new ($base,"Barca headlines","Newsfeed from fcbarcelona.com");
for ($i = 0; $i > @content; $i+=2) {
  rss_item ($base.$content[$i],$content[$i+1]);
}
rss_save('barca.rss');

exit(0);

La transformation est ici un peu compliquée par le fait que le code HTML du site visité est mal structuré. L'expression régulière utilisée pour localiser l'endroit où se trouvent les liens et leurs titre tiendra tant que le site ne sera pas complêtement modifié dans ses templates mais c'est déjà bien. Ce script est bien adapté pour les sites qui ne sont pas modifiés tous les jours mais où il peut y avoir plusieurs modifications dans la journée. Chaque nouvel élément arrive individuellement avec son titre et son URL.

Fil plus riche

Nous avons donc ce script qui récupère les titres et les URLs des différents nouveaux articles de FCBarcelona.com mais nous devions à chaque fois cliquer sur le lien proposé pour en savoir plus. Dans un effort supplémentaire, on peut offrir un fil encore plus riche et automatiser la récupération du contenu de la page en question. Cela nécessite une requête LWP supplémentaire par article mais cela offre un confort certain à l'utilisateur final.

#!perl
# Barca headlines and content RSS generator

use XML::RSS::SimpleGen;

# adresse du site
$base = "http://www.fcbarcelona.com";

# récupération de la page de référence
$html = get_url("$base/eng/home-page/home/home.shtml");

# recherche des titres
@content = ($html =~ /href=(\/eng\/noticias\/noticias\/n\d+\.\w+) class="b10b?">([^<]+)/g);

# début du RSS
rss_new($base,"Barca headlines");

for ($i = 0; $i < @content; $i+=2) {
  # récupération de chaque page liée
  $cite = get_url("$base".$content[$i]);
  $cite =~ s/\n/ /g;
  # extraction de nos informations
  $cite =~ /<!--Noticia completa-->(.*)<!--Fin noticia completa-->/;
  $notice = $1;
  # ajustement de la présentation
  $notice =~ s/<.*?>/ /g;
  $notice =~ s/\t+/ /g;
  # ajout de l'élément RSS
  rss_item ($base.$content[$i],$content[$i+1],$content[$i+1]."\n".$notice);
}
# sauvegarde du RSS
rss_save('barca.rss');

exit(0);

Filtrage de fil

Maintenant, imaginons que vous avez trouvé un fil où il y a des informations qui vous intérêssent mais où la majeure partie du contenu vous est inutile. Vous pouriez bien évidement configurer votre aggrégateur pour qu'il filtre mais vous pouvez tout aussi bien écrire un script qui le fait pour vous. Prenons l'exemple du fil de HAbett.org et imaginons maintenant que vous n'êtes intéressé que par les annonces d'images qui y sont faites. Cette fois, nous allons utiliser XML::RSS car nous avons besoin de parser (lire et comprendre) un fil avant d'en générer un autre.

#!perl -w
# HAbeTT.org image RSS selector

use XML::RSS;
use LWP::Simple 'get';
use strict;

# URL du fil
my $url = 'http://habett.org/interface/indexen.rss';

# préparation du nouveau fil
my $out = new XML::RSS(version => '1.0');
$out->channel(title => 'Images de HAbeTT', link => 'http://habett.org/images/',
 description => 'Dernières images publiées sur HAbeTT.org');

# récupération du fil original
my $feed = get($url) or die "Can't download $url $!";

# parsage du fil
my $in = new XML::RSS;
$in->parse($feed);

# boucle dans les éléments
foreach my $item (@{$in->{'items'}}) {
  # test avec expression régulière
  if ($item->{'title'} =~ /image/i) {
    # ajout du nouvel élément au nouveau fil
    $out->add_item(%$item);
  }
}

# sauvegarde du fil
$out->save('habetti.rss');

exit(0);

Comme vous le voyez c'est assez simple et cela ne nécessite qu'une simple expression régulière et une manipulation d'objet. Notez au passage que le parseur est indifférent à la version de fil RSS en entrée. C'est assez pratique pour restreindre un fil au contenu qui vous intérêsse et cela peut même vous conduire à réunir des fils de source et format différents dans un fil unique. On est alors tout près de la syndication.

La syndication

La syndication est la publication simultanée dans plusieurs endroits. Nous n'allons pas nous attarder sur le sujet mais vous devez savoir que de nombreuses techniques existent pour utiliser sur votre site les fils d'un autre site, dans la mesure où le droit d'auteur le permet. La réciproque est aussi vraie, vous pouvez proposer à vos contacts de mettre sur leur site les informations issues de votre fil.

Il y a des méthodes sur client sous forme de javascript mais celles-ci n'ont pas nos faveurs car le javascript peut être désactivé et l'on ne peut baser un système d'information sur son hypothétique présence et fiabilité. Parmi les méthodes serveur, la plus courante et pratique repose sur les Server Side Includes (SSI) faisant appel à un script transformant le fil RSS en HTML. Cette technique est courament utilisée pour faire connaître son contenu au travers de sites de tiers.

Un moyen simple de convertir un fil RSS en HTML peut se code ainsi :

$feed = get("http://habett.com/indexen.rss");
$rss = new XML::RSS;
$rss->parse($feed);
foreach $item (@{$rss->{'items'}}) {
  print "<li><a href=\"$item->{'link'}\">$item->{'title'}</a></li>\n";
}

Rédaction du fil RSS de votre site

Pour ce qui est du fil RSS de votre propre site, il y a plusieurs manières de le générer automatiquement ou en le programant en perl mais nous allons voir la génération à la main qui est plus intéressante.

Nous allons voir un fil RSS 1.0 utilisant uniquement deux modules, le Dublin Core MetaData Initiative qui permet d'ajouter des informations pratiques et de rendre votre fil plus riche, et le Syndication qui permet d'inclure des informations sur la diffusion de votre fil. Il existe de nombreux autres modules, normalisés ou non, qui permettent d'en rajouter encore et vous pouvez créer vos propres modules, mais nous n'irons pas plus loin ici.

Tout commence par un porologue XML classique avec déclaration des modules que l'on va utiliser.

<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 xmlns="http://purl.org/rss/1.0/"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
>

Viennent ensuite des déclarations relatives au sujet de votre fil RSS.

<channel rdf:about="http://habett.com/">
<title>haBEtt.com en français</title>
<link>http://habett.com/</link>
<description>Alternatif technico-existentiel pour le subtranet</description>
<image rdf:resource="http://habett.com/grafz/icon.png" />

Passons maintenant aux déclarations Dublin Core relative au fil en général.

<dc:language>fr-fr</dc:language>
<dc:rights>Droits d'auteur 1997-2004, Stéphane Roux.</dc:rights>
<dc:date>2004-10-09T06:13:43+00:00</dc:date>
<dc:publisher>Stéphane Roux</dc:publisher>
<dc:creator>habett@habett.org</dc:creator>
<dc:subject>Idées pour le subtranet et codage en perl.</dc:subject>

Pour finir, les informations du module de syndication qui sont relatives à la publication.

<syn:updatePeriod>daily</syn:updatePeriod>
<syn:updateFrequency>3</syn:updateFrequency>
<syn:updateBase>1970-01-01T00:00+00:00</syn:updateBase>

Nous avons fini de décrire le fil et de lui donner des attributs sous forme de meta informations. Nous pouvons passer à la description des différents éléments que nous allons présenter. Cette présentation des éléments se passe en deux temps : en premier nous listons tous les éléments RDF (Resource Description FrameWork) puis nous décrivons chacun de ses éléments. Voici une liste d'éléments:

<items>
 <rdf:Seq>
  <rdf:li rdf:resource="http://habett.com/papers/inforporn.html" />
  <rdf:li rdf:resource="http://habett.com/perl/dance.html" />
  <rdf:li rdf:resource="http://habett.com/perl/moteur.html" />
 </rdf:Seq>
</items>
</channel>

La pratique habituelle est d'avoir une quinzaine d'éléments. Avancons maintenant vers la description des éléments que nous avons déclarés. Chaque élément se présente sous cette forme, ses informations Dublin Core inclues.

<item rdf:about="http://habett.com/papers/infopron.html">
<title>Suite infoporn avec RSS</title>
<link>http://habett.com/papers/infoporn.html</link>
<description>Dans le cadre du projet infoporn, les fils RSS avec description, analyse, aggrégation, génération et édition.</description>
<dc:creator>Stéphane Roux</dc:creator>
<dc:subject>RSS, XML, aggrégation, génération, édition</dc:subject>
<dc:date>2004-10-09T09:38:41+00:00</dc:date>
</item>

Répétez ainsi pour chaque élément que vous avez annoncé. Juste avant de finir, nous allons ajouter un élément agréable, une icône pour notre fil, et un élément utile, une boîte de recherche locale.

<image rdf:about="http://habett.com/grafz/icon.png">
<title>haBEtt.com</title>
<url>http://habett.com/grafz/icon.png</url>
<link>http://habett.com/</link>
</image>

<textinput rdf:about="http://habett.com/cgi_bin/textracom.cgi">
<title>Recherche haBEtt.com</title>
<description>Recherchez dans le contenu de haBEtt.com</description>
<name>kw</name>
<link>http://habett.com/cgi-bin/textracom.cgi</link>
</textinput>

</rdf:RDF>

Vous savez maintenant écrire à la main un fil RSS 1.0. Il y a encore beaucoup de choses à étudier mais c'est déjà largement suffisant pour commencer. Une fois que votre fil est écrit, n'oubliez pas de le vérifier avec le Validateur du W3C, de faire des liens dessus à partir de votre site, et peut d'ajouter un petit <link rel="alternate" type="application/rss+xml" title="Mon fil" href="/index.rss"> dans le head de vos pages HTML.

Prospective

Avec un tel système, les nouvelles et les mises à jour arrivent sans aucun clic de souris et vous pouvez les lire avec la même facilité qui si vous naviguiez dans vos e-mails. Aujourd'hui, la pratique des fils RSS ne s'est pas encore généralisée mais elle présente une solution du futur. Pensez-y comme un système de notification automatique. Pensez-y comme une mailing list autogérée à laquelle vous vous inscrivez sans avoir à donner votre adresse e-mail à un tiers donc sans aucun risque de spam. Pensez-y plus généralement comme l'accomplissement de la prophécie du push media (qui remonte globalement à 1997) enfin à nos portes. Une solution moderne, peu complexe et économe en bande passante, à de nombreux problèmes.