真正最简单的联合






4.83/5 (12投票s)
2005年11月24日
5分钟阅读

38092

443
本文介绍了一个处理所有类型 RSS 供稿的 XSLT/JScript 框架,以及基于此框架构建的 HTML 新闻阅读器应用程序。
关于什么
近年来,RSS 已被证明是一种非常有用的数据分发技术。本文解决了在单个应用程序中处理不同标准 RSS 供稿的问题。对于任何构建自己的桌面聚合器或企业内部网环境的人来说,它都可能很有用。本文附带了一个新闻阅读器应用程序的骨架。
本文假定您使用 MSXML 3.0+ 作为 XML/XSLT 处理器。
标准化
RSS 唯一令人遗憾的是使用标准数量众多。您无法确定在浏览网络时会得到什么,因此您必须做好应对任何情况的准备。“任何情况”是
- RSS 0.90 - Netscape 创建的 RSS 技术最初版本,现在几乎已经绝迹。规范仍可在 PurplePages 存档找到。
- RSS 2.0/0.91-0.94 - 最流行的 RSS 分支。由 UserLand 的 Dave Winer 修改和简化(最初版本)而来。对于此格式,RSS 代表 Really Simple Syndication(真正简单的联合供稿)。随着 播客的引入,它变得更加流行。顺便说一句,不要被版本号迷惑:2.0 版本之前的版本是 0.94,而不是 1.0(这与 0.9x 完全不同)!规范可在 UserLand 站点中找到。
- RSS 1.0 - 严格来说并非标准,而是 RDF(Resource Description Framework,资源描述框架)的派生——由 W3C 开发的 Web 元数据标准。它冗长且可扩展(通过使用模块),比 v2.0 灵活得多。对于 0.90 和 1.0 版本,RSS 代表 RDF Site Summary(RDF 站点摘要)。规范可在 RSS-DEV 工作组的站点找到。
- Atom - 最新、因此也是最罕见的联合供稿格式。Atom 是(由 Internet Engineering Task Force 工作组)首次尝试开发的标准化、企业级联合供稿格式。完整的规范可在 IETF 站点找到:完整规范。
转换...
我们先从样式表开始。有三点值得注意
local-name()
XSLT 函数:当您需要去除所有命名空间内容以轻松获取节点名称时,它非常有用。xsl:value-of
指令的disable-output-escaping
选项:一个必备的 XSLT 元素。原因:网络大师倾向于在“description
”和“summary
”字段中嵌入有趣的 HTML 标记。通过将此选项设置为“yes”,我们可以保留标记,从而获得漂亮的页面,而不是一堆乱七八糟的标签。缺点是我们有一个安全问题:如果disable-output-escaping
中嵌入了恶意脚本,它可能会使您的本地计算机暴露于风险。通常,您需要某种类型的脚本<SCRIPT>
和<OBJECT>
标签的清理程序;如果没有,建议您仅从受信任的站点阅读 RSS 供稿。<xsl:text/>
指令:当您想手动去除不必要的空格时使用。这对于控制输出 HTML 代码的缩进非常有帮助。
前三个样式表可用于构建报纸风格的新闻供稿
列表 1.1:用于从 RSS 2.0/0.91 供稿构建报纸风格 HTML 页面的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="1.0"
indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body>
<div style=
"padding: 1em;background-color: #fafafa; border: 1px solid #cfcfcf;">
<xsl:for-each select="rss/channel/item">
<xsl:variable name="stl">
<xsl:text/>background-color: #efeff5;
border: 1px solid #cfcfcf;padding: 0em 1em 0em; margin:
<xsl:text/>
<xsl:choose>
<xsl:when test="position()=last()"> 0em</xsl:when>
<xsl:otherwise> 0em 0em 1em 0em</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<div>
<xsl:attribute name="style"><xsl:value-of select="$stl"/>
</xsl:attribute>
<p><h3 style="color:#800000"><xsl:value-of select="title"/></h3>
</p>
<p><xsl:value-of disable-output-escaping="yes"
select="description"/>
</p>
<xsl:variable name="pub" select="pubDate"/>
<xsl:if test="count($pub) > 0">
<p align="right"
style="margin:0; padding:0"><xsl:value-of select="pubDate"/>
</p>
</xsl:if>
<p style="margin:0; padding:0em 0em 1em 0em"><a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="link"/>
</xsl:attribute>
<xsl:value-of select="link"/>
</a></p>
</div>
</xsl:for-each>
</div>
</body></html>
</xsl:template>
</xsl:stylesheet>
列表 1.2:用于从 RSS 1.0 供稿构建报纸风格 HTML 页面的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body>
<div style=
"padding: 1em;background-color: #fafafa; border: 1px solid #cfcfcf;">
<xsl:for-each select="*/*[local-name()='item']">
<xsl:variable name="stl">
<xsl:text/>background-color: #efeff5;
border: 1px solid #cfcfcf;padding: 0em 1em 0em; margin:
<xsl:text/>
<xsl:choose>
<xsl:when test="position()=last()"> 0em</xsl:when>
<xsl:otherwise> 0em 0em 1em 0em</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<div>
<xsl:attribute name="style"><xsl:value-of select="$stl"/>
</xsl:attribute>
<p><h3 style="color:#800000">
<xsl:value-of select="./*[local-name()='title']"/>
</h3></p>
<p><xsl:value-of disable-output-escaping="yes"
select="./*[local-name()='description']"/>
<br/><br/>
<xsl:variable name="pub" select="*[local-name()='date']"/>
<xsl:variable name="pub_date"
select="concat(substring($pub, 0, 11), ', ',
substring($pub, 12, 8), ' (GMT+',
substring($pub, 21, 5), ')')"/>
<xsl:if test="count($pub) > 0">
<div align="right" style="margin:0em; padding:0em 0em 0em 0em;">
<xsl:value-of select="$pub_date"/></div>
</xsl:if>
<a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="./*[local-name()='link']"/>
</xsl:attribute><xsl:value-of select="./*[local-name()='link']"/>
</a></p>
</div>
</xsl:for-each>
</div>
</body></html>
</xsl:template>
</xsl:stylesheet>
列表 1.3:用于从 Atom 供稿构建报纸风格 HTML 页面的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body>
<div style=
"padding: 1em;background-color: #fafafa; border: 1px solid #cfcfcf;">
<xsl:for-each select="*/*[local-name()='entry']">
<xsl:variable name="stl">
<xsl:text/>
background-color: #efeff5;
border: 1px solid #cfcfcf;padding: 0em 1em 0em; margin:
<xsl:text/>
<xsl:choose>
<xsl:when test="position()=last()"> 0em</xsl:when>
<xsl:otherwise> 0em 0em 1em 0em</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<div>
<xsl:attribute name="style"><xsl:value-of select="$stl"/>
</xsl:attribute>
<p><h3 style="color:maroon">
<xsl:value-of select="*[local-name()='title']"/></h3></p>
<p><xsl:value-of disable-output-escaping="yes"
select="*[local-name()='summary']"/></p>
<xsl:variable name="pub" select="*[local-name()='updated']"/>
<xsl:variable name="pub_date" select=
"concat(substring($pub, 0, 11), ', ', substring($pub, 12, 8))"/>
<xsl:if test="count($pub)>0">
<p align="right" style="margin:0; padding:0;">
<xsl:value-of select="$pub_date"/>
</p>
</xsl:if>
<p style="margin:0; padding:0em 0em 1em 0em;"><a target="_blank">
<xsl:attribute name="href">
<xsl:value-of
select="*[local-name()='link']/@*[local-name()='href']"/>
</xsl:attribute>
<xsl:value-of
select="*[local-name()='link']/@*[local-name()='href']"/>
</a></p>
</div>
</xsl:for-each>
</div>
</body></html>
</xsl:template>
</xsl:stylesheet>
下一个感兴趣的点是供稿中找到的所有标题列表——大纲。此列表中的每个项都将是一个指向 JavaScript “navTo”函数的链接,其数字参数等于该项在列表中的位置。
列表 2.1:用于从 RSS 2.0/0.91 供稿检索项目列表的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body><ul style="margin-left:25">
<xsl:for-each select="rss/channel/item">
<li><a href="javascript:navTo('{position()}')">
<font style="size:-1;color:#800000">
<xsl:value-of select="title"/>
</font>
</a><br/></li>
</xsl:for-each>
</ul></body></html>
</xsl:template>
</xsl:stylesheet>
列表 2.2:用于从 RSS 1.0 供稿检索项目列表的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="1.0"
indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body><ul style="margin-left:25">
<xsl:for-each select="*/*[local-name()='item']">
<li><a href="javascript:navTo('{position()}')">
<font style="size:-1;color:#800000">
<xsl:value-of select="./*[local-name()='title']/text()"/>
</font>
</a><br/></li>
</xsl:for-each>
</ul></body></html>
</xsl:template>
</xsl:stylesheet>
列表 2.3:用于从 Atom 供稿检索项目列表的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="/">
<html><body><ul style="margin-left:25">
<xsl:for-each select="*/*[local-name()='entry']">
<li><a href="javascript:navTo('{position()}')">
<font style="size:-1;color:#800000">
<xsl:value-of select="./*[local-name()='title']/text()"/>
</font>
</a><br/></li>
</xsl:for-each>
</ul></body></html>
</xsl:template>
</xsl:stylesheet>
最后一组样式表负责转换单个新闻项。请注意,这些转换不能应用于原始 RSS 文件;在使用它们之前,您必须以编程方式提取所需项,并将其应用于其中一个样式表。
列表 3.1:用于表示 RSS 2.0/0.91 供稿中独立新闻项的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="*">
<div style="padding: 0em 1em 0em;
background-color: #fafafa; border: 1px solid #cfcfcf;">
<p><h3 style="color:#800000"><xsl:value-of select="title"/></h3></p>
<div style="padding: 0em 1em 0em; margin: 0em;
background-color: #efeff5; border: 1px solid #cfcfcf;">
<p><xsl:value-of disable-output-escaping="yes" select="description"/>
</p>
<xsl:variable name="pub" select="pubDate"/>
<xsl:if test="count($pub)>0">
<p align="right" style="margin:0em; padding:0em 0em 1em 0em;">
<xsl:value-of select="pubDate"/></p>
</xsl:if>
</div>
<p><a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="link"/>
</xsl:attribute>
<xsl:value-of select="link"/>
</a></p>
</div>
</xsl:template>
</xsl:stylesheet>
列表 3.2:用于表示 RSS 1.0 供稿中独立新闻项的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="*">
<div style="padding: 0em 1em 0em;
background-color: #fafafa; border: 1px solid #cfcfcf;">
<p><h3 style="color:#800000">
<xsl:value-of select="./*[local-name()='title']"/>
</h3></p>
<div style="padding: 0em 1em 0em; margin: 0em;
background-color: #efeff5; border: 1px solid #cfcfcf;">
<p><xsl:value-of disable-output-escaping="yes"
select="./*[local-name()='description']"/></p>
<xsl:variable name="pub" select="*[local-name()='date']"/>
<xsl:variable name="pub_date"
select="concat(substring($pub, 0, 11), ', ',
substring($pub, 12, 8),
' (GMT+', substring($pub, 21, 5), ')')"/>
<xsl:if test="count($pub) > 0">
<p align="right" style="margin:0em; padding:0em 0em 1em 0em;">
<xsl:value-of select="$pub_date"/>
</p>
</xsl:if>
</div>
<p><a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="./*[local-name()='link']"/>
</xsl:attribute><xsl:value-of select="./*[local-name()='link']"/>
</a></p>
</div>
</xsl:template>
</xsl:stylesheet>
列表 3.3:用于表示 Atom 供稿中独立新闻项的 XSLT 样式表
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"
version="1.0" indent="yes" encoding="iso-8859-1"/>
<xsl:template match="*">
<div style="padding: 0em 1em 0em;
background-color: #fafafa; border: 1px solid #cfcfcf;">
<p><h3 style="color:#800000">
<xsl:value-of select="*[local-name()='title']"/>
</h3></p>
<xsl:variable name="cnt" select="*[local-name()='content']">
</xsl:variable>
<xsl:if test="count($cnt)>0">
<div style="padding: 0em 1em 0em 1em; margin: 0em;
background-color: #efeff5; border: 1px solid #cfcfcf;">
<p><xsl:value-of disable-output-escaping="yes"
select="*[local-name()='content']"/></p>
<xsl:variable name="pub" select="*[local-name()='updated']"/>
<xsl:variable name="pub_date"
select="concat(substring($pub, 0, 11), ', ',
substring($pub, 12, 8))"/>
<xsl:if test="count($pub)>0">
<p align="right" style="margin:0em; padding:0em 0em 1em 0em;">
<xsl:value-of select="$pub_date"/>
</p>
</xsl:if>
</div>
</xsl:if>
<xsl:if test="count($cnt)=0">
<p style="padding: 1em; margin: 0em;
background-color: #efeff5; border: 1px solid #cfcfcf;">
<xsl:value-of disable-output-escaping="yes"
select="*[local-name()='summary']"/>
</p>
</xsl:if>
<p><a target="_blank">
<xsl:attribute name="href">
<xsl:value-of
select="*[local-name()='link']/@*[local-name()='href']"/>
</xsl:attribute>
<xsl:value-of
select="*[local-name()='link']/@*[local-name()='href']"/>
</a></p>
</div>
</xsl:template>
</xsl:stylesheet>
……以及阅读
在对 RSS 文件进行任何操作之前,您需要知道它属于哪个标准,对吗?我们通过分析 <xml-stylesheet>
的子节点来做到这一点。
列表 4.1:提取 RSS 标准
function whatStd(rssdocument)
{
var rssroot =
rssdocument.documentElement.selectSingleNode("/*");
var rsssdtd = rssroot.baseName;
switch(rsssdtd)
{
case "rss":
return "rss2";
case "RDF":
return "rss1";
case "feed":
return "atom";
default:
return "";
}
}
这段代码(以及以下所有代码)的坏处是它大量使用了 Microsoft 对 W3C XML API 的扩展。作为解决方案,您可以简单地提取 DocumentElement
的 firstChild
。
列表 4.2:提取 RSS 频道信息
var rss_title;
switch(standard)
{
case "atom":
rss_title = xml.documentElement.selectSingleNode(
"/*/*[local-name()='title']");
break;
case "rss1":
rss_title = xml.documentElement.selectSingleNode(
"/*/*[local-name()='channel']/*[local-name()='title']");
break;
case "rss2":
rss_title = xml.documentElement.selectSingleNode(
"/*/channel/title");
break;
}
var rss_link;
switch(standard)
{
case "atom":
rss_link = xml.documentElement.selectSingleNode(
"/*/*[local-name()='link']/@*[local-name()='href']");
break;
case "rss1":
rss_link = xml.documentElement.selectSingleNode(
"/*/*[local-name()='channel']/*[local-name()='link']");
break;
case "rss2":
rss_link = xml.documentElement.selectSingleNode(
"/*/channel/link");
break;
}
rsstitle.innerHTML =
"<a target=\"_blank\" title=\"Opens in new window\" href=\"" +
rss_link.text +
"\"><font color=\"maroon\" size=\"4\"><b>" +
rss_title.text + "</b></font></a>";
提取了频道信息后,提取单个供稿项将非常容易。这是我们要做的事情。
列表 4.3:提取新闻项
function navTo(where)
{
if(rssFile != "")
{
var rss_item;
switch(standard)
{
case "atom":
rss_item = xml.documentElement.selectSingleNode(
"/*/*[local-name()='entry'][" + where + "]");
break;
case "rss1":
rss_item = xml.documentElement.selectSingleNode(
"/*/*[local-name()='item'][" + where + "]");
break;
case "rss2":
rss_item = xml.documentElement.selectSingleNode(
"/*/channel/item[" + where + "]");
break;
default:
rss_item = null;
}
if(rss_item)
{
var item_i =
rss_item.transformNode(xsl_i.documentElement);
contentcell.vAlign = "Top";
content.innerHTML = item_i;
...
}
}
}
请注意 xsl_i
(用于转换),这是我之前描述过的项提取样式表。Where
是一个数字的字符串表示——项在供稿中的位置。
就这些了。随时给我发电子邮件,提出您的建议/意见/错误报告。
链接
教程
- Mark Nottingham 的教程。
- Jonathan Eisenzopf 关于创建 RSS 文件的文章。
- WebReference.com 上的 RSS 入门介绍。
- 博客、RSS 新闻阅读器和 Atom:第一部分,第二部分——各种新闻供稿标准的概述。
- 基于 XML 的 (we)blog 和 RSS 供稿 - 使用 RSS 和博客服务的绝佳示例。
- AtomEnabled.org - 关于 Atom 标准的大量信息。
RSS 列表
工具和其他一切
- RSS-Specifications.com - 不仅有规范,还有您能从 RSS 中获得的一切。
- BlogSpace.com 的 RSS 区域 - 精美的文章/工具/链接集。
历史
- 2005 年 11 月 23 日
- 发布文章,样式表和新闻阅读器的第一个版本。
- 2006 年 2 月 13 日
- 代码清理,部分函数完全重写;
- XPath 查询已清理;
- 添加了“保存 HTML”功能;
- XSLT 样式表已优化/清理。
- 2006 年 4 月 25 日
- 添加了自动/手动供稿更新功能;
- 细微改进和 bug 修复。