从HTML新闻网站创建RSS频道
即使你以前从未听说过RSS这个缩写,你很可能过去使用过RSS。无论是通过slashdot.org的slashboxes,还是我们的use.perl.org新闻摘要,前提都是相同的——RSS,或“远程网站摘要”,是一种用于提供网站上最新新闻概览的方法。
RSS是一种基于XML的格式,其中网站信息以简化新闻为几个关键元素的方式进行描述。在我们将要运行的示例中,我们将特别关注标题和链接标签。如果你对RSS的具体内容感兴趣,可以在netscape.com上了解更多信息,并查看完整的规范;然而,对于本教程的目的,我将集中讨论如何使用Perl操纵RSS,并忽略RSS的内部机制。
因此,你有一个需要RSS源的新闻网站;或者让我们假设有一个你想转换为RSS格式的新闻网站。O'Reilly自己的Meerkat网站创建了其他网站新闻的RSS描述,并以新闻票的形式展示了来自整个互联网的最新新闻。在本教程中,我们不会使用Meerkat的相同方法,但我们将使用类似的技术来提供BBC新闻网站的RSS源。
那么,我们的Perl脚本需要做什么才能将网站上的标题转换为RSS频道呢?我们将处理三个主要任务
- 下载页面以便脚本工作
- 解析页面上的HTML以提供有意义的摘要信息
- 将摘要信息编码为RSS
对于以前使用过庞大的CPAN模块网络的任何人来说,听到这里不会感到惊讶,因为这里有一些模块可以帮助我们完成每个任务。我们将使用LWP::Simple
来处理页面下载,使用HTML::TokeParser
将下载的HTML解析为有意义的英语,并使用XML::RSS
从标题和链接创建RSS频道。
让我们开始编写代码。在声明我们将要使用的模块之后...
use strict;
use LWP::Simple;
use HTML::TokeParser;
use XML::RSS;
..我们准备好使用每个模块创建我们将要使用的每个对象。请注意,虽然LWP::Simple
使用过程式接口,但HTML::TokeParser
和XML::RSS
都有面向对象的接口。如果你不熟悉Perl中的面向对象,Simon Cozens最近关于面向对象Perl的文章可能非常有帮助。
# First - LWP::Simple. Download the page using get();.
my $content = get( "http://news.bbc.co.uk/" ) or die $!;
# Second - Create a TokeParser object, using our downloaded HTML.
my $stream = HTML::TokeParser->new( \$content ) or die $!;
# Finally - create the RSS object.
my $rss = XML::RSS->new( version => '0.9' );
我们的下一步是为脚本打下基础,将声明一些变量并设置RSS对象上的频道信息。每个RSS频道都包含一些元数据;也就是说,提供关于所编码数据的信息的数据。例如,它通常至少包含频道的名称、描述和一个URL链接。我们将通过在对象上调用channel
方法来设置元数据,如下所示
# Prep the RSS.
$rss->channel(
title => "news.bbc.co.uk",
link => "http://news.bbc.co.uk/",
description => "news.bbc.co.uk - World News from the BBC.");
# Declare variables.
my ($tag, headline, $url);
现在我们已经准备好了所有三个模块,可以继续进行第二步——从HTML页面获取纯文本信息。这是我们需要应用我们的分析技能来确定我们正在查看的网站的布局,以及如何找到我们想要提取的标题的地方。BBC的HTML布局复杂但可预测,遵循以下常规
- 存在一个
<div class="bodytext">
标签。 - 打开并关闭了一个
<a>
标签。 - 打开了一个
<a>
标签,其中包含我们想要抓取的 URL,链接到新闻文章的全文。 - 关闭了
<a>
标签,并打开了一个<b class="h1">
或<b class="h2">
标签。 - 我们的标题位于这个
<b>
标签和</b>
标签之间。
乍一看,获取 URL 和标题似乎会很麻烦,但 HTML::TokeParser
可以轻松处理页面。我们通过 HTML::TokeParser
获得的两个重要方法是 get_tag
和 get_trimmed_text
。 get_tag
会从当前位置向前跳转到指定的标签,而 get_trimmed_text
会从当前位置抓取到指定的结束位置的内容。因此,我们现在可以将对 BBC 布局的描述翻译成对 HTML::TokeParser
对象 $stream
的方法的描述。
# First indication of a headline - A <div> tag is present.
while ( $tag = $stream->get_tag("div") ) {
# Inside this loop, $tag is at a <div> tag.
# But do we have a "class="bodytext">" token, too?
if ($tag->[1]{class} and $tag->[1]{class} eq 'bodytext') {
# We do!
# The next step is an <a></a> set, which we aren't interested in.
# Let's go past it to the next <a>.
$tag = $stream->get_tag('a'); $tag = $stream->get_tag('a');
# Now, we're at the <a> with the headline in.
# We need to put the contents of the 'href' token in $url.
$url = $tag->[1]{href} || "--";
# That's $url done. We can move on to $headline, and <b>
$tag = $stream->get_tag('b');
# Now we can grab $headline, by using get_trimmed_text
# up to the close of the <b> tag.
# We want <b class="h1"> or <b class="h2">.
# A regular expression will come in useful here.
$headline = $stream->get_trimmed_text('/b') \
if ($tag->[1]{class} =~ /^h[12]$/);
我们正在接近目标。页面已下载,我们处于一个 while
循环中,将抓取页面上的每一组 URL 和标题对。剩下的只是将 $url
和 $headline
添加到我们的 RSS 频道;但首先,需要做一些整理。
# We need to escape ampersands, as they start entity references in XML.
$url =~ s/&/&/g;
# The <a> tags contain relative URLs - we need to qualify these.
$url = 'http://news.bbc.co.uk'.$url;
# And that's it. We can add our pair to the RSS channel.
$rss->add_item( title => $headline, link => $url);
}
}
在每个 <div>
标签的迭代结束时,我们的 $rss
对象将包含页面上的所有标题和链接。现在我们只需要将其保存到某个地方,这可以通过 $rss->save
方法完成。
$rss->save("bbcnews.rss");
执行我们的脚本后,我们会在当前目录中得到一个名为 'bbcnews.rss' 的文件,任何 RSS 解析器都可以处理它——例如,[这里](http://printf.net/evobbc.jpg) 是我的当前邮件客户端 Evolution,它将我们的数据添加到“摘要”功能中。
如果我们像所有优秀的 RSS 源一样将其放在网上,我们甚至可以通过 Slashboxes 或 use.perl 新闻框访问它。但是,如果您想使其可通过网络访问,最好定期通过 cron
任务或类似的方式创建 RSS 文件,而不是使用 CGI,尤其是如果您认为 RSS 将比目标网站更新得更频繁。
当然,如果您为您的网站创建 RSS 源,您将对自己的数据有更大的控制权。例如,如果您使用 XML 生成网站,您可以使用 XML 转换技术来生成 RSS——但这将是另一个教程的内容。现在,祝您玩得开心,快乐地抓取信息!
标签
反馈
这篇文章有什么问题吗?请通过在 GitHub 上打开问题或拉取请求来帮助我们。