[Bielefeld-pm] RSS parsen

Nils Diewald nils at diewald-online.de
Mi Mär 30 08:28:11 PDT 2011


Oh - Perlanet kannte ich noch nicht. Wobei das schon sehr nach einer 
All-in-One-Lösung ausschaut, die vielleicht nicht unbedingt zum 
Reader-Building geeignet ist (aber OPML unterstützt - das ist schon nett).
Neben RSS sollte ja auch ATOM unterstützt werden, das macht das Parsing 
noch etwas komplexer, wobei die RSS-Varianten sich gar nicht so arg 
unterscheiden (es gibt nur zwei oder drei nennenswert verschiedene 
Versionen).
Ich habe ein Script angefügt, das ich für RSS mal geschrieben hatte 
(allerdings etwas anders einsetzte).
Die Google-Reader-Alternative, von der ich gestern sprach, ist übrigens 
Newsblur: http://www.newsblur.com/
Es ist auch OS, aber kommerziell: https://github.com/samuelclay/NewsBlur
Viele Grüße,
Nils



Am 30.03.2011 16:20, schrieb Renée Bäcker:
> Ich setze für planet.perl-magazin.de das Modul Perlanet [1] ein. Das ist
> sehr einfach einzurichten!
>
> http://search.cpan.org/dist/Perlanet/
>
>
> On 30.03.2011 15:49, taulmarill at xgn.de wrote:
>> Hallo zusammen,
>>
>> nachdem sich gestern der Bedarf nach einer OS Alternative zu Google Reader
>> herauskristallisiert hat, möchte ich zuerst mal an den größeren Kreis der
>> Personen die hier mitlesen noch mal die Frage stellen, ob so etwas
>> irgendwo bekannt ist.
>> Für den Fall dass es doch nicht anders geht, als selber was zu bauen, habe
>> ich mich mit dem Thema schon einmal flüchtig beschäftigt. Das größte
>> Problem dürften die vielen, verschiedenen Versionen sein (alleine neun
>> RSS-Versionen, bei Atom habe ich noch nicht geschaut). Allerdings lässt
>> sich das mit Mojo::DOM ganz gut abfrühstücken, hier mal ein minimales
>> Beispiel:
>>
>> -----<start>-----
>> use strict;
>> use warnings;
>>
>> use Mojo::UserAgent;
>>
>> my $ua = Mojo::UserAgent->new;
>>
>> my $feed = $ua->get('http://www.heise.de/ix/news/news.rdf')->res->dom;
>>
>> if ( my $title = $feed->at('channel title') ) {
>>      print $title->text . "\n";
>> }
>>
>> if ( my $description = $feed->at('channel description') ) {
>>      print $description->text . "\n";
>> }
>>
>> if ( my $items = $feed->find('item title') ) {
>>      $items->each(sub {print "=>  " . shift->text . "\n"});
>> }
>> -----<end>-----
>>
>> Da sollte sich eigentlich was basteln lassen...
>>
>>
>> Gruß,
>> Jürgen
>>
>> _______________________________________________
>> Bielefeld-pm mailing list
>> Bielefeld-pm at pm.org
>> http://mail.pm.org/mailman/listinfo/bielefeld-pm
>>
>>
>>
>

-------------- nächster Teil --------------
#!/usr/bin/perl
use strict;
use warnings;
use Mojolicious::Lite;

app->secret('jgfhfztrezhf5rvb brvbg');
get '/test' => sub {
    shift->render('template' => 'test')
};


get '/' => sub {
    my $self = shift;
    my $c = $self->ua;
    $c->max_redirects(3);
    my $xml = $c->get('http://netzwertig.com/feed/')->res->dom;

# TODO: Paths can be relative or without the correct base!
# http://cyber.law.harvard.edu/rss/rss.html

    my ($title, $channel_date);

    if ($title = $xml->at('channel title')) {
	$title = $title->text;
    };

    if ($channel_date = $xml->at('channel pubdate')) {
	$channel_date = $channel_date->text;
    };

    my @array;
    $xml->find('item')->each(sub {
	my %item;
	$item{'title'} = $_->at('title') ? $_->at('title')->text : '';

	$item{'creator'} = $_->at('creator') ? $_->at('creator')->text : '';
	$item{'link'} = $_->at('link') ? $_->at('link')->text : '';
	$item{'pubDate'} = $_->at('pubdate') ?  $_->at('pubdate')->text : '';

	# content namespace!
	my $content = '';
	my $content_ns = 'http://purl.org/rss/1.0/modules/content/';

	if (($content = $_->at('encoded')) && ($content->namespace eq $content_ns)) {
	    $item{'content'} = clean_content($content->text);
	} elsif ($content eq $_->at('description')) {
	    $item{'content'} = clean_content($content->text);
	};

	my $image;
        # <enclosure type="image/jpeg" url="http://www.spiegel.de/images/image-146217-thumbsmall-nztv.jpg"/>
	if ($image = $_->at('enclosure[type^="image"]')) {
	    $item{'image'} = $image->attrs->{'url'} || '';
	} else {
	    $item{'image'} = undef;
	};


	push(@array, \%item);
			     });
    $self->render(
	template     => 'index',
	title        => $title,
	channel_date => $channel_date,
	items        => \@array);
};

sub clean_content {
    my $string = shift;
    if ($string =~ /[<>]/) {
	my $INSECURE_ATTRNAME_RE =
	    qr/(?i:id|class|style|on(?:blur|change|(?:dbl)?click|error|
                   focus|key(?:down|press|up)|mouse(?:down|move|out|over|up)|
                   resize|select|unload))/x;
	# (?:[^:]+:[^:]+)
	my $INSECURE_ATTR_RE = qr/$INSECURE_ATTRNAME_RE\s*=\s*(?:"([^"]*)"|'([^']*)')/;

	$string =~ s/\s*$INSECURE_ATTR_RE\s*//g;
	$string =~ s/(href)\s*=\s*("([^"]*)"|'([^']*)')/href=$2/gi;

	my $content = Mojo::DOM->new->parse($string);
	$content->find('script')->each(
	    sub { shift->replace('') }
	);
	
	$content->find('a')->each(
	    sub {
		my $a = shift;
		my $href = $a->attrs->{'href'};
		if ($href =~ /^\s*javascript:/i) {
		    $a->replace($a->inner_xml);
		};
	    }
	    );
	return $content->to_xml;
    } else {
	return $string;
    };
};

app->start;

__DATA__
@@ layouts/standard.html.ep
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
  <head><title><%= title %></title></head>
  <body>
    <%= content %>
  </body>
</html>

@@ index.html.ep
% layout 'standard';

<h1><%= title %></h1>
<p><%= $channel_date %></p>
% foreach (@$items) {
<div style="background-color: #efe; border: 2px solid #898; margin: 2em; padding: 2em;">
  <% if ($_->{image}) { %><img src="<%= $_->{image} %>" alt="<%= $_->{title} %>" style="float: right;" /><% } %>

  <h3><a href="<%= $_->{link} %>"><%= $_->{title} %></a></h3>

  <p style="padding-left: 3em; color: grey;">von <%= $_->{creator} %> am <%= $_->{pubDate} %></p>

  <% if ($_->{'content'}) { %>
    <%== $_->{'content'} %>
  <% } %>
</div>
% }


Mehr Informationen über die Mailingliste Bielefeld-pm