简单生活
返回文章列表
4368 字22 分钟

RSS2.0 规范文档

设计开发#RSS

前些天,修改 Windsphoto 相册时,突发奇想,增加 RSS 订阅的功能。以前从来没有真正接触过 XML,难度相当大。最后弄了半天,还是不符合规范,在 Foxmail 等离线阅读器上没办法订阅到,不过总算可以在一些在线阅读上订阅了。

RSS 的规范的确很严格,对于 web 开发人员来说,比较有难度的应该是时间格式的转换。

什么是 RSS?

RSS 是一种站点内容聚合的格式。

它的名字是*Really Simple Syndication *的的简写。

RSS 是 XML 的一种。所有的 RSS 文档都遵循 XML 1.0 规范, 该规范发布在W3C网站上。

在一个 RSS 文档中,最外层是一个元素,这个元素必须规定 version 属性,该属性明确了本文档遵从何种 RSS 版本规范。如果一个 RSS 文档以这个规范来表示,那么它的 version 属性就必须是 2.0。

元素只有一个子元素,包含了关于这个频道(元数据)和它的内容的一些信息。

样本文件

这里有一些 RSS 样本文件:RSS 0.91, 0.92 和 2.0。

注意这些样本文件所指向的链接地址和服务器可能已经不再存在。在撰写 0.91 文档的时候,这个 0.91 的样本文件就已经创建了。维护一个样本文件的历史也许是一个不错的主意。

关于本文档

本文档完成于 2002 年秋天,版本为 2.0.1。

它包含从 RSS 0.91 规范(2000 年)开始的所有的修改和添加,以及包含在RSS 0.92 (2000 年 12 月)和 RSS 0.94(2002 年 8 月)中的新的特性。

本文档中首先介绍必须的和可选的频道元素;接着介绍了的子元素。最后回答了一些经常碰到的问题,并提供了未来的发展路线和 RSS 扩展的指导方针。

必需的频道元素

下面是一个必须包含的频道(channel)元素的列表,每一个都有一个简单的描述、一个例子、应该出现的位置和更详细描述的链接地址。

01.● title

名称:title

描述:频道的名称。它表明别人如何访问你的服务。如果你有一个与你的 RSS 文件内容一致的 HTML 网站,你的 title 元素值应该与你的网站的标题相同。

例子:GoUpstate.com 的新闻大字标题。

02.● link

名称:link

描述:对应频道的网站的 URL 链接地址。

例子:http://www.goupstate.com/ 。

03.● description

名称:description

描述:关于频道的描述。

例子:The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site。

可选的频道元素

下面是一个可选的频道(channel)元素的列表。

01.● language

名称:language

描述:频道使用的语言。比如,在一个网站上,允许聚合所有的意大利语站点到相应的分组。对于这个元素,可使用的值,参阅 W3C 定义的 清单。

例子:en-us。

02.● copyright

名称:copyright

描述:频道内容的版权声明。

例子:Copyright 2002, Spartanburg Herald-Journal

03.● managingEditor

名称:managingEditor

描述:频道内容责任编辑的电子邮件地址。

例子:geo@herald.com (George Matesky)

04.● webMaster

名称:webMaster

描述:频道技术支持人员的电子邮件地址。

例子:betty@herald.com (Betty Guernsey)

05.● pubDate

名称:pubDate

描述:频道内容发布的日期。所有的日期和时间都必须遵循 RFC 822规范, 但年份可以用2个或4个字母表示(首选4个字母)。

例子:Sat, 07 Sep 2002 00:00 GMT

06.● lastBuildDate

名称:lastBuildDate

描述:频道内容的最后修改时间。

例子:Sat, 07 Sep 2002 09:42 GMT

07.● category

名称:category

描述:指定频道所属的一个或多个分类。遵循与 item 级 category 元素相同的规则。

例子:Newspapers

08.● generator

名称:generator

描述:表明生成频道的程序名称的字符串。

例子:MightyInHouse Content System v2.3

09.● docs

名称:docs

描述:指向该 RSS 文件所用格式说明文档的 URL 链接地址。

例子:http://blogs.law.harvard.edu/tech/rss。

10.● cloud

名称:cloud

描述:允许通过注册一个 cloud 来处理获得频道的更新通知,并为 rss 种子实现一个轻量级的发布订阅协议,

例子:

11.● ttl

名称:ttl

描述:ttl 是 Time to live 的缩写,表示生存时间。它表示频道从源更新之前可以缓存的时间。

例子:60。

12.● image

名称:image

描述:指定一个可以在频道中显示的 GIF、JPEG 或者 PNG 图像。

例子:。

13.● rating

名称:rating

描述:频道的 PICS 内容分级信息。

例子: 。

14.● textInput

名称:textInput

描述:指定一个可以在频道中显示的文本输入框。

例子:。

15.● skipHours

名称:skipHours

描述:提示聚合器,可以跳过那些小时的时间段。

例子:。

16.● skipDays

名称:skipDays

描述:提示聚合器,可以跳过那些天的时间段。

例子:。

**的子元素 **

是 的一个可选子元素, 它本身包含了三个必须的和三个可选的子元素。

是一个 GIF、JPEG 或 PNG 图像文件的 URL 链接地址,该图像代表整个频道。

用于描述上面的图像,当频道在 HTML 中显示时,用于 HTML 语言中的<img>的 alt 属性。 <link>是要连接的站点的 url,当显示频道时,图像的连接指向该站点。(在实际中,<title>和<link>应该与频道的<title>和<link>有相同的值)。 <p>可选的元素包括<width>和<height>,它们是数字类型,指定图像的宽度和高度,单位为像素。</p> <p><description>就是 link 的 TITLE 属性中文本,它将在调用网页时显示出来。</p> <p>图像宽度的最大值为 144,默认值为 88。</p> <p>图像高度的最大值为 400,默认值为 31。</p> <p><strong><channel>的子元素<cloud></strong></p> <p><cloud> 是 <channel>的一个可选子元素。</p> <p>它指定一个可以支持 rssCloud 接口的 web 服务,rssCloud 接口可以在 HTTP-POST、XML-RPC 或 SOAP1.1 中实现。</p> <p>它的目的是允许通过注册一个 cloud 来处理获得频道的更新通知,从而为 RSS feeds 实现一个轻量级的发布订阅协议.</p> <cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="myCloud.rssPleaseNotify" protocol="xml-rpc" /> <p>在这个例子中,为了请求频道通知,你需要发送一个 XML-RPC 消息到 rpc.sys.com 的 80 端口,路径为/RPC2。调用的程序为为 myCloud.rssPleaseNotify。</p> <p><strong><channel>子元素<ttl></strong></p> <p><ttl><channel>的一个可选子元素。</p> <p>ttl 是 Time to live 的缩写,表示生存时间。它表示频道从源重新更新之前可以缓存的时间。这使得 rss 源可以被一个支持文件共享的网络所管理,例如<a href="http://www.gnutellanews.com/information/what_is_gnutella.shtml" target="_blank" rel="noopener noreferrer nofollow"><strong>Gnutella</strong></a>。</p> <p>例子: <ttl>60</ttl></p> <p><strong><channel>的子元素<textInput></strong></p> <p>频道可以选择包含一个<textInput>子元素,它本身包含了四个必须的子元素。</p> <title>--文本输入区域提交按钮的标签。 <p><description>–文本输入区域的描述。</p> <p><name>–文本输入区域中文本对象的名称。</p> <link>--处理文本输入请求的 CGI 脚本的 URL 链接地址。 <p>使用<textInput>元素的目的看起来有些神秘。你可以用它提供一个搜索引擎输入框,或让读者提供反馈信息。许多聚合器忽略该元素。</p> <p><strong><item>的元素</strong></p> <p>一个频道可以包含许多<item>元素。一个项目可以代表一个“故事” ——比如说一份报纸或杂志上的故事;如果是这样的话,那么项目的描述则是故事的摘要,项目的链接则指向整个故事的链接位置。一个项目也可以本身是完整的,如果是这样的话,项目的描述就包含了文本(整体以 HTML 格式编码是可以的),而链接和标题可以省略。项目的所有元素都是可选的,但是至少要包含一个标题(title)或描述(description)。</p> <p>01.● title</p> <p>名称:title</p> <p>描述:item 的标题。</p> <p>例子:Venice Film Festival Tries to Quit Sinking</p> <p>02.● link</p> <p>名称:link</p> <p>描述:item 的 URL 链接地址。</p> <p>例子:<a href="http://nytimes.com/2004/12/07FEST.html" target="_blank" rel="noopener noreferrer nofollow">http://nytimes.com/2004/12/07FEST.html</a></p> <p>03.● description</p> <p>名称:description</p> <p>描述:item 的摘要。</p> <p>例子:Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged.</p> <p>04.● author</p> <p>名称:author</p> <p>描述:item 作者的电子邮件地址。</p> <p>例子:。</p> <p>05.● category</p> <p>名称:category</p> <p>描述:包含 item 在一个或多个分类中。</p> <p>例子:。</p> <p>06.● comments</p> <p>名称:comments</p> <p>描述:与 item 相关的评论的 URL 链接地址。</p> <p>例子:。</p> <p>07.● enclosure</p> <p>名称:enclosure</p> <p>描述:item 附加的媒体对象。</p> <p>例子:。</p> <p>08.● guid</p> <p>名称:guid</p> <p>描述:可以唯一确定 item 身份的字符串。</p> <p>例子:。</p> <p>09.● pubDate</p> <p>名称:pubDate</p> <p>描述:item 发布的时间。</p> <p>例子:。</p> <p>10.● source</p> <p>名称:source</p> <p>描述:rss 频道来源。</p> <p>例子:。</p> <p><strong><item>的子元素<source></strong></p> <p><source>是<item>的一个可选子元素。</p> <p>它的值是 item 所属 rss 频道的名称,从 title 衍生而来。它有一个必须包含的属性 url, 该属性链接到 XML 序列化源。</p> <p><source url="http://www.tomalak.org/links2.xml">Tomalak’s Realm</source></p> <p>该元素的作用是提高链接的声望,从而进一步推广新闻项目的源头。它可以用在聚合器的 Post 命令中。当从聚合器中通过一个 webblog 编辑工具提交一个 item 时,<source>应该能够被自动生成。</p> <p><strong><item>的子元素<enclosure></strong></p> <p><enclosure>是<item>的一个可选子元素。</p> <p>它有三个必须的属性。url 属性表明 enclosure 的位置,length 属性表明它的字节大小,而 type 属性则指出它的标准 MIME 类型。</p> <p>这里的 url 必须为一个 http url。</p> <enclosure url="http://www.scripting.com/mp3s/weatherReportSuite.mp3" length="12216320" type="audio/mpeg" /> <p>它的 use-case 说明请参见<strong>这里</strong>。</p> <p><strong><item>的子元素<category></strong></p> <p><category>是<item>的一个可选子元素。</p> <p>它有一个可选属性 domain,该属性是一个用来定义分类法的字符串。</p> <p>该节点的值是一个斜杠分割的字符串,它用来表明在指定的分类法中的层次位置。处理器可以为分类的识别建立协定。以下是两个例子:</p> <p><category>Grateful Dead</category></p> <p><category domain="http://www.fool.com/cusips">MSFT</category></p> <p>你可以根据需要为不同的域包含很多<category>元素,并且可以在相同域的不同部分拥有一个交叉引用的 item。</p> <p><strong><item>的子元素<pubDate></strong></p> <p><pubDate> 是<item>的一个可选子元素。</p> <p>它的值是一个 <strong>日期</strong>, 表明项目发布的时间。如果它是一个将来的日期,则聚合器在日期到达之前可以选择不显示该项目。</p> <p><pubDate>Sun, 19 May 2002 15:21 GMT</pubDate></p> <p>**<item>的子元素<guid> **</p> <p><guid>是<item>的一个可选子元素。</p> <p>guid 是 globally unique identifier 的缩写。它是一个可以唯一识别这个<item>的字符串。在发布之后,聚合器可以选择使用该字符串判断这个<item>是否是新的。</p> <p><guid><a href="http://some.server.com/weblogItem3207" target="_blank" rel="noopener noreferrer nofollow">http://some.server.com/weblogItem3207</a></guid></p> <p>guid 没有特定的语法规则。聚合器必须将它们当作一个字符串来处理。生成具有唯一性的字符串 guid 取决于种子的源头。</p> <p>如果 guid 元素有 isPermaLink 属性,并且值为真,解释器就会认为它是 item 的 permalink。permalink 是一个可在 web 浏览器中打开的 url 链接,它指向<item>节点所描述的完整 item。例如:</p> <p><guid isPermaLink="true"><a href="http://inessential.com/2002/09/01.php#a2" target="_blank" rel="noopener noreferrer nofollow">http://inessential.com/2002/09/01.php#a2</a></guid></p> <p>isPermaLink 是可选属性,默认值为真。如果值为假,guid 将不会被认为是一个 url 或指向任何对象的 url。</p> <p><strong><item>的子元素<comments></strong></p> <p><comments>是<item>的一个可选子元素。</p> <p>如果出现,它指向与 item 相关的评论的 url。</p> <p><comments><a href="http://ekzemplo.com/entry/4403/comments" target="_blank" rel="noopener noreferrer nofollow">http://ekzemplo.com/entry/4403/comments</a></comments></p> <p>**<item>的子元素<author> **</p> <p><author>是<item>的一个可选子元素。</p> <p>它是 item 作者的电子邮件地址 l。对于通过 rss 传播的报纸和杂志,作者可能是写该 item 所描述的文章的人。对于聚集型 webblogs,作者可能不是责任编辑或站长。对于个人维护的 webblog,忽略<author>节点是有意义的。</p> <p><author><a href="mailto:lawyer@boyer.net">lawyer@boyer.net</a> (Lawyer Boyer)</author></p> <p><strong>注释</strong></p> <p>RSS 限制<link> 和 <url>元素的数据首字母为非空格字符。这些元素的数据必须以 <a href="http://www.iana.org/assignments/uri-schemes" target="_blank" rel="noopener noreferrer nofollow"><strong>IANA-registered</strong></a> URI 方案规定的格式开始,如http://, https://, news://, mailto ftp://等。在 RSS2.0 规范之前,RSS 规范只允许 http:// 和 ftp://,然而在实践中,其他的 URI 方案被内容开发者广泛使用,并被聚合器所支持。聚合器也许对它们支持的 URI 方案有一些限制,而内容开发者不应该假定所有的聚合器都支持所有的 URI 方案。</p> <p>在 RSS 0.91 规范中,各种元素都被限制为 500 或 100 个字符。在一个符合 0.91 规范的频道中,不能超过 15 个 <item> 元素。在 RSS 0.92 和以后的规范中,不再有这些字符长度或者 XML 级别的限制。处理器也许强加一些它们自己的限制,产生者也许有自己的一些参数选择,它们可以规定在一个频道中,不超过一定数目的<item>元素,或者字符串都限制在一定的长度。</p> <p>如上所述,在 RSS 2.0 规范中,对于一个目录系统,当链接一个频道到它的标识中时,使用基于频道级别的分类特征。例如,如果链接一个频道到它的 Syndic8 标识,则将包括一个分类元素作为频道的子元素,它有域 Syndic8 属性,同时在 Syndic8 数据库中为你的频道确定这个标识。正确的分类元素脚本应该是 <category domain="Syndic8">1765</category>。</p> <p>一个经常被问到的问题是关于< guid> 如何和 <link>进行区别。它们指的是相同的事情吗?在一些内容系统中是,但在别的内容系统中可能不是。在一些系统中,<link> 是一个网络日志项的永久链接。然后在别的系统中,每一个<item>都是一个较长文章的摘要,<link>指向这篇文章,而 <guid>则是这个网络日志入口的永久链接。在所有的情况下,建议提供<guid>,如果可能的话,并使它成为一个永久链接。这使聚合着在内容发生变化时,也不会出现重复项目成为可能。</p> <p>如果你对 RSS 2.0 规范的格式有任何问题,请向由 Sjoerd Visscher 维护的电子邮件列表<a href="http://groups.yahoo.com/group/RSS2-Support/" target="_blank" rel="noopener noreferrer nofollow"><strong>RSS2-Support</strong></a>发送邮件。这个邮件列表不是一个技术辩论的列表,而是一个针对作者和开发人员在创建和使用 RSS 2.0 格式的内容时提供技术支持的列表。</p> <p><strong>RSS 的扩展</strong></p> <p>RSS 起源于 1999 年,目标是成为一个简单、易于理解的数据格式。在它逐渐成为一种流行格式之后,开发者想在一个名字空间中使用模块对它进行扩展,正像 W3C <a href="http://www.w3.org/TR/REC-xml-names/" target="_blank" rel="noopener noreferrer nofollow"><strong>定义</strong></a>的那样。</p> <p>RSS 遵循简单的规则,增加了它的能力。一个 RSS feed 可以包含不是在本页中描述的内容,而只是在一个名字空间中定义的那些元素。</p> <p>本文档中定义的元素不是一个名字空间本省的元素,因此,RSS2.0 从某种意义上来讲,和原来的版本是兼容的,即一个 0.91 或者 0.92 版本的文件也是一个有效的 2.0 版本文件。如果 RSS2.0 的元素是在一个名字空间中,那么这种约束将被打破,即一个 0.9x 版本的文件不可能是一个有效的 2.0 版本的文件。</p> <p><strong>发展方向</strong></p> <p>RSS 决不是一个完美的格式,但是它现在已经非常流行,并得到广泛的支持。要成为一个固定的规范,RSS 需要很长的一段时间。这项工作的目的是帮助 RSS 成为一个固定的事情,同时促进和培育围绕它进行的开发的市场的增长,并为新的聚合格式铺平道路。因此,为了实用的目的,RSS 规范将被冻结在 2.0.2 版本。我们可以预期的可能的 2.0.2 或者 2.0.3 等版本,都只是出于澄清规范的目的,而不是在格式上增加新的特征。后续的工作应该集中在模块化、名字空间的使用和在完全新的聚合格式中用新的名字等方面。</p> <p><strong>许可协议和作者</strong></p> <p>RSS 2.0 是在遵循 the Attribution/Share Alike Creative Commons <a href="http://creativecommons.org/licenses/by-sa/1.0/" target="_blank" rel="noopener noreferrer nofollow"><strong>许可协议</strong></a>的基础上由 the Berkman Center for Internet & Society at Harvard Law School 提供。本文档的作者是 Dave Winer,UserLand software 的创始人,也是 Berkman Center 的员工。(全文完)</p> </div></article><div class="mt-4 space-y-4"><section aria-labelledby="post-information-title" class="rounded-panel border border-border bg-panel overflow-hidden"><div class="px-5 py-5 tablet:px-8 tablet:py-6"><h2 id="post-information-title" class="m-0 text-base font-bold">文章信息</h2><div class="mt-4 flex items-center gap-3"><span class="grid size-8 flex-none place-items-center rounded-md bg-accent-soft text-accent-strong"><svg class="shrink-0" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><text x="12" y="14.5" text-anchor="middle" font-size="6.5" font-weight="700" stroke="none" fill="currentColor">CC</text></svg></span><span class="min-w-0"><span class="block text-xs text-muted">许可协议</span><span class="block text-sm leading-6 text-text-soft">CC BY-NC-SA 4.0</span></span></div></div></section><nav aria-label="文章导航" class="rounded-panel border border-border bg-panel grid grid-cols-2 overflow-hidden"><a class="group flex min-h-24 min-w-0 items-center gap-3 border-r border-border px-4 py-4 transition-colors hover:bg-accent-soft tablet:gap-4 tablet:px-6" href="/post/im_not_hacker.html" aria-label="上一篇:我不是黑客!"><svg class="shrink-0 flex-none rotate-180 text-accent-strong" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg><span class="min-w-0"><span class="block text-xs font-medium text-accent-strong">上一篇</span><span class="mt-1 line-clamp-2 block text-sm leading-6 text-text-soft">我不是黑客!</span></span></a><a class="group flex min-h-24 min-w-0 items-center justify-end gap-3 px-4 py-4 text-right transition-colors hover:bg-accent-soft tablet:gap-4 tablet:px-6" href="/post/the_cruelest_april.html" aria-label="下一篇:写在最残忍的四月"><span class="min-w-0"><span class="block text-xs font-medium text-accent-strong">下一篇</span><span class="mt-1 line-clamp-2 block text-sm leading-6 text-text-soft">写在最残忍的四月</span></span><svg class="shrink-0 flex-none text-accent-strong" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a></nav></div></div><script type="module">var e=()=>{},t=()=>{e();let t=Array.from(document.querySelectorAll(`[data-toc-link]`)),n=document.querySelector(`[data-toc-indicator]`),r=t.map(e=>{let t=decodeURIComponent(e.hash.slice(1)),n=document.getElementById(t);return n?{heading:n,link:e}:void 0}).filter(e=>e!==void 0);if(r.length===0||!n)return;let i=()=>{let e=r.flatMap(({heading:e},t)=>{let n=e.getBoundingClientRect();return n.top<window.innerHeight&&n.bottom>0?[t]:[]});if(e.length===0){let t=0,n=1/0;r.forEach(({heading:e},r)=>{let i=Math.abs(e.getBoundingClientRect().top);i<n&&(t=r,n=i)}),e=[t]}let t=r[e[0]].link,i=r[e[e.length-1]].link,a=t.closest(`ol`)?.getBoundingClientRect(),o=t.getBoundingClientRect(),s=i.getBoundingClientRect();a&&(n.style.top=`${o.top-a.top}px`,n.style.height=`${s.bottom-o.top}px`,n.classList.remove(`opacity-0`)),r.forEach(({link:t},n)=>{let r=e.includes(n);t.classList.toggle(`text-accent-strong`,r),t.querySelector(`[data-toc-marker]`)?.classList.toggle(`bg-accent`,r),n===e[0]?t.setAttribute(`aria-current`,`location`):t.removeAttribute(`aria-current`)})},a=0,o=()=>{cancelAnimationFrame(a),a=requestAnimationFrame(i)};i(),document.addEventListener(`scroll`,o,{passive:!0}),window.addEventListener(`resize`,o,{passive:!0}),e=()=>{cancelAnimationFrame(a),document.removeEventListener(`scroll`,o),window.removeEventListener(`resize`,o)}};document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,t,{once:!0}):t(),document.addEventListener(`astro:page-load`,t);</script></div></main><footer class="mx-auto mt-4 w-layout-width max-w-page py-6 text-center text-xs leading-7 text-muted-light tablet:w-layout-width-tablet tablet:py-8 tablet:text-sm wide:w-layout-width-wide"><nav class="mb-1 flex flex-wrap justify-center gap-x-2" aria-label="页脚导航"><a class="transition-colors hover:text-accent-strong" href="/privacy">隐私政策</a><span aria-hidden="true">|</span><a class="transition-colors hover:text-accent-strong" href="/rss.xml">RSS</a><span aria-hidden="true">|</span><a class="transition-colors hover:text-accent-strong" href="/sitemap-index.xml">Sitemap</a></nav><p class="m-0">© 2026 狼的旋律. All Rights Reserved.</p><p class="mt-1 mb-0">Powered by <a class="font-semibold text-text-soft transition-colors hover:text-accent-strong" href="https://astro.build" target="_blank" rel="noreferrer">Astro</a> & <a class="font-semibold text-text-soft transition-colors hover:text-accent-strong" href="https://github.com/CnBarrier404/astro-theme-misthaven" target="_blank" rel="noreferrer">Misthaven</a></p></footer></div></div><script type="module">var e=async()=>{let e=document.querySelector(`[data-comment-view-counts]`);if(!e||e.dataset.viewCountsReady)return;e.dataset.viewCountsReady=`true`;let t=e.dataset.server,n=e.dataset.site;if(!t||!n)return;let r=[...new Set([...document.querySelectorAll(`[data-post-list-item][data-post-path]`)].map(e=>e.dataset.postPath).filter(e=>!!e))];if(r.length!==0)try{let e=new URL(`${t.replace(/\/+$/,``)}/api/v2/stats/page_pv`,window.location.origin);e.search=new URLSearchParams({page_keys:r.join(`,`),site_name:n}).toString();let i=await fetch(e);if(!i.ok)throw Error(`Artalk stats request failed: ${i.status}`);let{data:a}=await i.json(),o={};for(let e of r){let t=a?.[e]??0;typeof t==`number`&&Number.isFinite(t)&&(o[e]=t)}document.dispatchEvent(new CustomEvent(`post-view-counts`,{detail:{counts:o}}))}catch(e){console.error(`Failed to load Artalk view counts`,e)}};e(),document.addEventListener(`astro:page-load`,e);</script></body></html>