Vu que le site est maintenant hébergé chez un vrai hébergeur, je suis obligé de lancer mon script Perl par un script PHP, appelé regulièrement par le site http://crontab.fr. Je remercie d'ailleurs les administrateurs de Lost-Oasis qui ont accepté d'installer les modules Perl dont j'avais besoin.

Création des tables SQL

--
-- Table structure for table `rss_items`
--

CREATE TABLE `rss_items` (
  `id` int(11) NOT NULL auto_increment,
  `id_who` bigint(20) unsigned NOT NULL default '0',
  `title` varchar(255) default NULL,
  `link` varchar(255) default NULL,
  `date` date default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `ITEM` (`id_who`,`link`)
) TYPE=MyISAM;

--
-- Table structure for table `rss_sites`
--

DROP TABLE IF EXISTS `rss_sites`;
CREATE TABLE `rss_sites` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `name` varchar(25) NOT NULL default '',
  `url` varchar(255) NOT NULL default '',
  `lastmodified` varchar(255) default NULL,
  `etag` varchar(255) default NULL,
  `inactive` tinyint(3) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`)
) TYPE=MyISAM;

fetch_rss.pl

C'est le script central, celui qui récupère les RSS, les traite, et les stocke en base de données. Les nouveautés sont: récupération des sites à visiter depuis la base de données, utilisation des headers HTTP, ce qui oblige d'utiliser une API plus complexe, reconnexion à la base de données en cas de perte de la connexion.

#!/usr/bin/perl
#

use LWP::UserAgent;
use XML::LibXML;
use DateTime;
use DateTime::Format::HTTP;
use DateTime::Format::DBI;
use DBI;

use strict;
use warnings;

my $db_type = "mysql";
my $db_user = "username";
my $db_pass = "password";
my $db_hostname = "dbhostname";
my $db_database = "database";
my $db_table_items = "rss_items";
my $db_table_sites = "rss_sites";

my $xmlparser = XML::LibXML->new();
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;


my $dsn = "DBI:$db_type:database=$db_database;host=$db_hostname";
my $dbh = DBI->connect($dsn, $db_user, $db_pass)
	or die "La connexion à la base de données a échouée : " . $DBI::errstr;
$dbh->{mysql_auto_reconnect} = 1 if $db_type eq "mysql";

my $friends = $dbh->selectall_hashref("SELECT id, name, url, lastmodified, etag FROM $db_table_sites WHERE inactive=0", 'id')
	or die "Erreur lors de la récupération des sites : " . $dbh->errstr;

my $dateparser = DateTime::Format::DBI->new($dbh);

my $sth = $dbh->prepare("INSERT IGNORE INTO $db_table_items SET id_who=?, title=?, link=?, date=?")
	or die "Erreur lors de la préparation sql : " . $dbh->errstr;
my $sth2 = $dbh->prepare("UPDATE $db_table_sites SET etag=?, lastmodified=? WHERE id=?")
	or die "Erreur lors de la préparation sql : " . $dbh->errstr;

foreach my $friend_id (keys %$friends) {
	my $friend = $friends->{$friend_id};
	next unless $friend->{url};

	my %headers = ();
	$headers{"If-Modified-Since"} = $friend->{lastmodified} if ($friend->{lastmodified});
	$headers{"If-None-Match"} = $friend->{etag} if ($friend->{etag});

	my $response = $ua->get(
		$friend->{url},
		%headers
	);

	print($friend->{name}, " : 304\n"), next if $response->code == 304;
	warn("N'a pas réussi à prendre le rss de " . $friend->{name} . " : " . $response->status_line), next
		unless $response->is_success;

	my $content = $response->content;
	my $etag = $response->header("Etag");
	my $lastmodified = $response->header("Last-Modified");
	$sth2->execute($etag, $lastmodified, $friend_id)
		or die "L'éxecution SQL a échoué : " . $sth->errstr;
	
	my $rss;
	eval { $rss = $xmlparser->parse_string($content) };
	do { warn $friend->{name}, " : ", $@; next; } if $@;

	my @items = $rss->getElementsByLocalName("item");
	foreach my $item (@items) {
		my ($title, $link, $date);
		
		my $child = $item->firstChild();
		while ($child) {
			next unless $child->localname;
			$title = $child->textContent if $child->localname =~ /^title$/;
			$link = $child->textContent if $child->localname =~ /^link$/;
			$date = $child->textContent if $child->localname=~ /^date$/;
			$date = $child->textContent if $child->localname=~ /^pubDate$/;
		} continue {
			$child = $child->nextSibling();
		}

		if ($date) {
			$date = DateTime::Format::HTTP->parse_datetime($date);
		} else {
			$date = DateTime->now();
		}
		
		$sth->execute($friend_id, $title, $link, $dateparser->format_datetime($date))
			or die "L'éxecution SQL a échoué : " . $sth->errstr;
	}
}

$sth->finish;
$dbh->disconnect();

fetch_rss.php

Ce fichier exécute fetch_rss.pl et affiche le résultat si le script a échoué ou si le paramètre debug est placé à 1.

<?
$dir = dirname(FILE);
exec("$dir/../scripts/fetch_rss.pl 2>&1", $output, $retval);
if ($retval != 0 || $_GET'debug' == 1) {
	echo "<br>$retval<br>";
	echo nl2br(join("\n", $output));
}
?>

Récupération depuis un script

Je récupère ensuite les données avec la requête suivante :

SELECT rs.name who, ri.link link, ri.title title
	FROM rss_items ri, rss_sites rs
	WHERE (TO_DAYS(ri.date) > (TO_DAYS(CURDATE()) - 7))
	AND rs.id = ri.id_who
	AND rs.inactive = 0
	ORDER BY who ASC, ri.date DESC, ri.id DESC