At the Forge - 使用 RSS 进行聚合
当我刚开始使用 Web 时,任何架设网站的人都会向 Tim Berners-Lee 发送电子邮件,提供 URL 和关于网站内容的简要描述。Tim 会回复一封简短的个人便条,并更新他的网站主列表,任何拥有浏览器的人都可以检索该列表。Web 社区的积极参与者会定期查看该列表——及其由制作 Mosaic 浏览器的同一批人发布的后续列表——以查找新的和更新的网站,以免错过任何信息。
快进十多年,Web 显然已经变得过于庞大,任何人都不可能手动维护新网站的列表。即使这是可能的,也没有人能够阅读每天上线的海量新内容中的一小部分。此外,现在有成千上万个网络日志或博客,其中许多博客会频繁更新,这使得任务变得更加困难。
一种解决方案是使用浏览器的书签。但是过了一段时间后,每天检查书签,更不用说一天检查几次书签,就变成了一件苦差事。如果每个网站都可以指示其内容何时发生更改,以便您仅在必要时访问,那就太好了。
这种见解并不新鲜;宣布 Web 内容更改的想法已经存在了好几年。但我必须承认,直到几个月前,当我每天开始访问书签中的几个网站时,我才开始意识到自己有多么落后于时代。通过利用 RSS 聚合器——即,一个查看来自各个网站的 RSS 订阅源并在有更新时提醒我的程序——我能够在更少的时间内完成更多的工作。
本月,我们将讨论流行的 RSS(真正简单的联合供稿或 RDF 站点摘要)格式系列,研究它可能有哪些用途以及它是如何创建的。
RSS 最初是 Netscape 的创意,这家互联网软件公司后来被 AOL 收购(并在很大程度上被肢解)。Netscape 希望在单个页面上向人们提供来自多个来源的新闻。他们通过发布 RSS 0.90 的规范来实现这一目标。任何有兴趣通过 Netscape 门户发布新闻的人都需要以 RSS 格式进行发布。Netscape 的系统将从相关网站检索此 RSS 文档并发布结果。
尽管 RSS 0.90 引发了一场革命,但它也相当复杂。当时的 Userland Software 负责人 Dave Winer 将 RSS 转变为一个简单的规范,将其重命名为 RSS 0.91,并开始在他的 Weblog scripting.com 上谈论它。突然之间,RSS 0.91 无处不在;Dave 的橙色 XML 按钮,指示您可以从网站获取 RSS 订阅源,变得非常流行。在几年之内,也出现了支持其他版本的 RSS 订阅源。RSS 1.0 由 Web 上的一组开发人员开发,而由 Dave 协调的 RSS 2.0 被视为 0.9x 的升级版。
如果您一直在关注这段历史,您可能会得出结论,现在有三种不同的联合供稿格式称为 RSS。除了版本号以及不同版本之间的一些明显的相似之处外,这三种是不同的格式。
在许多方面,RSS 类似于 HTML 和 HTTP,它们最初是由一小群人编写的易于理解、易于实施的标准。这三种标准在过去几年中都被迫变得相当成熟,在这个过程中失去了一些灵活性和简单性。
RSS 0.91 是其中最简单的,并且仍然相当流行。所有内容都位于 <rss> 元素内,该元素标识其版本并包含单个 <channel> 元素。几个必需的标签(title、link、description、language 和 image)之后是一个或多个 <item> 元素。每个项目都有自己的标题、链接和描述。例如,这是来自我的 Weblog 的一个简单 RSS 订阅源
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd"> <rss version="0.91"> <channel> <title>Altneuland</title> <link>http://altneuland.lerner.co.il/</link> <description>Reuven's Weblog</description> <item> <title>Independence Day</title> <link>http://altneuland.lerner.co.il//40</link> </item> <item> <title>Linux desktops for the masses? Ha!</title> <link>http://altneuland.lerner.co.il//39</link> </item> </channel> </rss>
如果您检查上面的 RSS 订阅源,您会看到它不符合我之前描述的 RSS 0.91 规范。具体来说,它缺少 channel 中必需的 language 和 image 元素,并且每个 item 中都缺少 description 元素。不幸的是,这并不令人意外;正如 HTML 在其早期阶段的情况一样,软件作者经常偷工减料,生成对于大多数用途来说足够好的输出。实际上,COREBlog(在撰写本文时,我正在使用它来制作我的 Weblog)似乎也偷工减料了,生成了一个可用但不合格的 RSS 0.91 订阅源。
如果您想生成合法的 RSS 订阅源,您可能应该使用许多可用于大多数流行语言的开源模块之一。例如,Perl 开发人员可以使用 XML::RSS 模块,该模块可从任何 CPAN 镜像站点获得(请参阅在线资源部分)。
要使用此模块创建 RSS 订阅源,我们可以编写一个如下所示的简单程序
#!/usr/bin/perl use strict; use diagnostics; use warnings; use XML::RSS; my $url = "http://altneuland.lerner.co.il/"; my $rss = new XML::RSS (version => '0.91'); $rss->channel(title => 'Altneuland', link => $url, language => 'en', description => "Reuven Lerner's Weblog"); $rss->add_item(title => 'Being scared', link => "$url/43/index_html", description => 'Blog entry' ); print $rss->as_string;
我们程序的开头是创建一个新的 XML::RSS 对象,指定我们要使用 RSS 标准的 0.91 版本。然后我们指定要定义的各个项目,并且可以省略 image 标签。尽管 XML::RSS 模块允许我们从 channel 描述符中省略任何或所有标签,但省略其中一些标签(例如 title 和 link)是没有意义的。
然后,我们将各个项目逐个添加到 channel,直到我们完成所有项目。此时,我们可以生成 RSS 输出,如下所示
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd"> <rss version="0.91"> <channel> <title>Altneuland</title> <link>http://altneuland.lerner.co.il/</link> <description>Reuven Lerner's Weblog </description> <language>en</language> <item> <title>Being scared</title> <link>http://altneuland.lerner.co.il/43/index_html</link> <description>Blog entry</description> </item> </channel> </rss>
大多数生成 RSS 订阅源的程序不会像我上面那样,在个案基础上调用 $rss->add_item()。如果我们正在联合供稿 Weblog、商业新闻订阅源或其他频繁更新的网站,我们可能会通过循环遍历目录中的一组文件或(更好的是)关系数据库中的行来创建 RSS 订阅源。
例如,以下代码片段将检索过去 24 小时内发布到 PostgreSQL 中假设的 weblog_entries 表中的所有 Weblog 条目
# Get all entries from the latest 24 hours my $sql = "SELECT entry_id, title, link, description FROM weblog_entries WHERE when_entered >= (NOW() - interval '1 day')"; # Prepare the SQL statement my $sth = $dbh->prepare($sql); # Execute the SQL statement my $result = $sth->execute; # Iterate through resulting rows while (my $rowref = $sth->fetchrow_arrayref) { my ($id, $title, $link, $description) = @$rowref; $rss->add_item(title => $title, link => $link, description => $description ); }
这演示了将 Weblog 存储在关系数据库中的众多优势之一。一旦条目存储在数据库中,就可以轻松添加新功能,例如联合供稿。尽管 XML::RSS 提供了限制联合供稿文章数量为设定数量的功能(以及示例代码,在其 perldoc 在线文档中),但这似乎更适合数据库的工作,在数据库中,LIMIT 修饰符可以设置返回的最大行数。
RSS 1.0 是对 RSS 0.91 的回应,它更紧密地将其与各种万维网联盟 (W3C) 标准(包括 RDF)联系起来。版本号可能会让您认为 1.0 是 0.91 的升级版;但是,两者(不幸的是)是独立且不协调的。0.91(及其后续版本 RSS 2.0)是由 Dave Winer 根据开发人员社区的意见编写的,而 1.0 是由一个开放的开发人员联盟编写的。RSS 0.91 和 2.0 比 1.0 与它们中的任何一个都有更多的共同点,这不足为奇地导致了一些混乱。
RDF,即 W3C 定义的资源开发框架,是语义 Web 项目的一部分,该项目希望使 Web 既能被计算机理解,又能被人理解。这需要标准化元数据,即伴随网站输出的不可见描述。RDF 是对此类标准化的一种尝试。
因此,RSS 1.0 将联合供稿与 RDF 联系起来,并在此过程中添加了 XML 命名空间的使用。XML 命名空间允许我们将不同的 XML 定义组合到一个文档中。
要创建符合 RSS 1.0 的联合供稿订阅源,我们只需要对上面的程序进行一个简单的更改,即更改 XML::RSS 上 new 调用中的版本号
my $rss = new XML::RSS (version => '1.00');
实际上,如果我们进行此更改,生成的 RSS 订阅源看起来略有不同
<?xml version="1.0" encoding="UTF-8"?> <rdf:RDF xmlns="http://purl.org/rss/1.0/" > <channel rdf:about="http://altneuland.lerner.co.il/"> <title>Altneuland</title> <link>http://altneuland.lerner.co.il/</link> <description>Reuven Lerner's Weblog </description> <dc:language>en</dc:language> <items> <rdf:Seq> <rdf:li rdf:resource= "http://altneuland.lerner.co.il/43/index_html" /> </rdf:Seq> </items> </channel> <item rdf:about= "http://altneuland.lerner.co.il/43/index_html"> <title>Being scared</title> <link>http://altneuland.lerner.co.il/43/index_html</link> <description>Blog entry</description> </item> </rdf:RDF>
在此输出中需要注意几件事,首先是几个命名空间的定义和使用,这些命名空间是通过 xmlns 属性引入的,然后是 RDF 特有属性的使用,例如 rdf:about 和 rdf:resource。
但是上面并没有充分体现 RSS 1.0 的优点,它允许我们指定大量其他参数。例如,我们可以通过在调用 $rss->channel() 中添加一个 syn 部分来设置有关我们网站联合供稿更新频率的信息;RSS 1.0 还包括对 Dublin Core 的支持,Dublin Core 是一种越来越流行的文档标记标准方法。
正如我们所见,好消息是,假设您使用的是不错的工具,那么创建或解析 RSS 1.0 并不比 RSS 0.91 困难多少。但是,有些人认为 RSS 1.0 的复杂性是不必要的。
实际上,在多次尝试就 RSS 1.0 达成共识之后,许多开发人员联合起来开发现在称为 Atom 的东西。尽管对 Atom 的讨论将不得不等到下一次,但这促使 RSS 阵营(由 Winer 领导)生产了 RSS 2.0。
您可以通过更改 new 调用中的版本号来生成与 RSS 2.0 兼容的订阅源
my $rss = new XML::RSS (version => '2.0');
您必须说 2.0;2 或 2.00 都不起作用,因为版本检查使用字符串比较,而不是数字比较。
RSS 2.0 是什么样子的?嗯,您可能会感到惊讶
<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:blogChannel= "http://backend.userland.com/blogChannelModule"> <channel> <title>Altneuland</title> <link>http://altneuland.lerner.co.il/</link> <description>Reuven Lerner's Weblog </description> <language>en</language> <item> <title>Being scared</title> <link>http://altneuland.lerner.co.il/43/index_html</link> <description>Blog entry</description> </item> </channel> </rss>
这看起来很像 RSS 0.91,因此,似乎是 RSS 1.0 的精简版本。但是,当我们记住 RSS 2.0 是 0.91 的后续版本,并且旨在修复其某些缺陷,同时保持小巧、易于实施和灵活时,它就变得更加明显了。
RSS 2.0 包含许多优于 0.91 的改进,最重要的是使用命名空间作为添加新功能的模块的想法。RSS 2.0 没有像 1.0 那样定义或使用几乎那么多的命名空间,但那是因为它没有尝试实现 RDF。
部分原因是由于有人批评他个人拥有 RSS 2.0 规范的版权,Winer 将所有权移交给了哈佛大学。据推测,Winer 将继续在 RSS 2.0 的开发中发挥重要作用,但他将不再是关于使用或扩展的最终仲裁者。
但是,这种分裂似乎是最终的;现在有一个 Atom 阵营和一个 RSS 阵营,而且我发现很难相信他们会走到一起。但是,考虑到他们为自己设定的相互冲突的目标,这不应该令人感到惊讶——毕竟,您不能期望在同一个规范中同时拥有灵活性和易于实施性。
本月,我们研究了当前使用的不同类型的 RSS,并比较了它们不同的风格和项目目标。幸运的是,想要生成一个基本联合供稿订阅源的人不需要非常努力。尽管程序员可以添加一些特定于版本的字段,但对于所有版本的 RSS 来说,基本知识都是相同的,即使是那些表面上不兼容的版本也是如此。当然,生成的 RSS 订阅源看起来可能大相径庭,具体取决于使用的版本。
在下一期专栏中,我们将研究新兴的 Atom 联合供稿格式,它作为 RSS 的竞争对手正在迅速发展。完成此操作后,我们将研究如何构建我们自己的新闻聚合器,使我们能够解释和处理来自各种来源的联合供稿订阅源。我们还将考虑可以使用 RSS 的不同方式,以及聚合器如何提供比最新新闻和观点更多的信息。
本文的资源: /article/7702。
Reuven M. Lerner,一位长期的 Web/数据库顾问和开发人员,现在是西北大学学习科学项目的一年级研究生。他的 Weblog 是 altneuland.lerner.co.il,您可以通过 reuven@lerner.co.il 与他联系。