用Python的Django框架来制作一个RSS阅读器


Posted in Python onJuly 22, 2015

Django带来了一个高级的聚合生成框架,它使得创建RSS和Atom feeds变得非常容易。

什么是RSS? 什么是Atom?

RSS和Atom都是基于XML的格式,你可以用它来提供有关你站点内容的自动更新的feed。 了解更多关于RSS的可以访问 http://www.whatisrss.com/, 更多Atom的信息可以访问 http://www.atomenabled.org/.

想创建一个联合供稿的源(syndication feed),所需要做的只是写一个简短的python类。 你可以创建任意多的源(feed)。

高级feed生成框架是一个默认绑定到/feeds/的视图,Django使用URL的其它部分(在/feeds/之后的任何东西)来决定输出 哪个feed Django uses the remainder of the URL (everything after /feeds/ ) to determine which feed to return.

要创建一个 sitemap,你只需要写一个 Sitemap 类然后配置你的URLconf指向它。
初始化

为了在您的Django站点中激活syndication feeds, 添加如下的 URLconf:

(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
  {'feed_dict': feeds}
),

这一行告诉Django使用RSS框架处理所有的以 "feeds/" 开头的URL. ( 你可以修改 "feeds/" 前缀以满足您自己的要求. )

URLConf里有一行参数: {'feed_dict': feeds},这个参数可以把对应URL需要发布的feed内容传递给 syndication framework

特别的,feed_dict应该是一个映射feed的slug(简短URL标签)到它的Feed类的字典 你可以在URL配置本身里定义feed_dict,这里是一个完整的例子 You can define the feed_dict in the URLconf itself. Here's a full example URLconf:

from django.conf.urls.defaults import *
from mysite.feeds import LatestEntries, LatestEntriesByCategory

feeds = {
  'latest': LatestEntries,
  'categories': LatestEntriesByCategory,
}

urlpatterns = patterns('',
  # ...
  (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
    {'feed_dict': feeds}),
  # ...
)

前面的例子注册了两个feed:

  1.     LatestEntries``表示的内容将对应到``feeds/latest/ .
  2.     LatestEntriesByCategory``的内容将对应到 ``feeds/categories/ .

以上的设定完成之后,接下来需要自己定义 Feed 类

一个 Feed 类是一个简单的python类,用来表示一个syndication feed. 一个feed可能是简单的 (例如一个站点新闻feed,或者最基本的,显示一个blog的最新条目),也可能更加复杂(例如一个显示blog某一类别下所有条目的feed。 这里类别 category 是个变量).

Feed类必须继承django.contrib.syndication.feeds.Feed,它们可以在你的代码树的任何位置
一个简单的Feed

This simple example describes a feed of the latest five blog entries for a given blog:

from django.contrib.syndication.feeds import Feed
from mysite.blog.models import Entry

class LatestEntries(Feed):
  title = "My Blog"
  link = "/archive/"
  description = "The latest news about stuff."

  def items(self):
    return Entry.objects.order_by('-pub_date')[:5]

要注意的重要的事情如下所示:

  •     子类 django.contrib.syndication.feeds.Feed .
  •     title , link , 和 description 对应一个标准 RSS 里的 <title> , <link> , 和 <description> 标签.
  •     items() 是一个方法,返回一个用以包含在包含在feed的 <item> 元素里的 list 虽然例子里用Djangos database API返回的 NewsItem 对象, items() 不一定必须返回 model的实例 Although this example returns Entry objects using Django's database API, items() doesn't have to return model instances.

还有一个步骤,在一个RSS feed里,每个(item)有一个(title),(link)和(description),我们需要告诉框架 把数据放到这些元素中 In an RSS feed, each <item> has a <title> , <link> , and <description> . We need to tell the framework what data to put into those elements.

    如果要指定 <title> 和 <description> ,可以建立一个Django模板(见Chapter 4)名字叫 feeds/latest_title.html 和 feeds/latest_description.html ,后者是URLConf里为对应feed指定的 slug 。注意 .html 后缀是必须的。 Note that the .html extension is required.

    RSS系统模板渲染每一个条目,需要给传递2个参数给模板上下文变量:

  1.         obj : 当前对象 ( 返回到 items() 任意对象之一 )。
  2.         site : 一个表示当前站点的 django.models.core.sites.Site 对象。 这对于 {{ site.domain }} 或者 {{ site.name }} 很有用。

    如果你在创建模板的时候,没有指明标题或者描述信息,框架会默认使用 "{{ obj }}" ,对象的字符串表示。 (For model objects, this will be the __unicode__() method.

    你也可以通过修改 Feed 类中的两个属性 title_template 和 description_template 来改变这两个模板的名字。

    你有两种方法来指定 <link> 的内容。 Django 首先执行 items() 中每一项的 get_absolute_url() 方法。 如果该方法不存在,就会尝试执行 Feed 类中的 item_link() 方法,并将自身作为 item 参数传递进去。

    get_absolute_url() 和 item_link() 都应该以Python字符串形式返回URL。

    对于前面提到的 LatestEntries 例子,我们可以实现一个简单的feed模板。 latest_title.html 包括:

{{ obj.title }}

    并且 latest_description.html 包含:

{{ obj.description }}

    这真是 太 简单了!

一个更复杂的Feed

框架通过参数支持更加复杂的feeds。

For example, say your blog offers an RSS feed for every distinct tag you've used to categorize your entries. 如果为每一个单独的区域建立一个 Feed 类就显得很不明智。

取而代之的方法是,使用聚合框架来产生一个通用的源,使其可以根据feeds URL返回相应的信息。

Your tag-specific feeds could use URLs like this:

    http://example.com/feeds/tags/python/ : Returns recent entries tagged with python

    http://example.com/feeds/tags/cats/ : Returns recent entries tagged with cats

固定的那一部分是 "beats" (区域)。

举个例子会澄清一切。 下面是每个地区特定的feeds:

from django.core.exceptions import ObjectDoesNotExist
from mysite.blog.models import Entry, Tag

class TagFeed(Feed):
  def get_object(self, bits):
    # In case of "/feeds/tags/cats/dogs/mice/", or other such
    # clutter, check that bits has only one member.
    if len(bits) != 1:
      raise ObjectDoesNotExist
    return Tag.objects.get(tag=bits[0])

  def title(self, obj):
    return "My Blog: Entries tagged with %s" % obj.tag

  def link(self, obj):
    return obj.get_absolute_url()

  def description(self, obj):
    return "Entries tagged with %s" % obj.tag

  def items(self, obj):
    entries = Entry.objects.filter(tags__id__exact=obj.id)
    return entries.order_by('-pub_date')[:30]

以下是RSS框架的基本算法,我们假设通过URL /rss/beats/0613/ 来访问这个类:

    框架获得了URL /rss/beats/0613/ 并且注意到URL中的slug部分后面含有更多的信息。 它将斜杠("/" )作为分隔符,把剩余的字符串分割开作为参数,调用 Feed 类的 get_object() 方法。

    在这个例子中,添加的信息是 ['0613'] 。对于 /rss/beats/0613/foo/bar/ 的一个URL请求, 这些信息就是 ['0613', 'foo', 'bar'] 。

    get_object() 就根据给定的 bits 值来返回区域信息。

    In this case, it uses the Django database API to retrieve the Tag . Note that get_object() should raise django.core.exceptions.ObjectDoesNotExist if given invalid parameters. 在 Beat.objects.get() 调用中也没有出现 try /except 代码块。 函数在出错时抛出 Beat.DoesNotExist 异常,而 Beat.DoesNotExist 是 ObjectDoesNotExist 异常的一个子类型。

    为产生 <title> , <link> , 和 <description> 的feeds, Django使用 title() , link() , 和 description() 方法。 在上面的例子中,它们都是简单的字符串类型的类属性,而这个例子表明,它们既可以是字符串, 也可以是 方法。 对于每一个 title , link 和 description 的组合,Django使用以下的算法:

        试图调用一个函数,并且以 get_object() 返回的对象作为参数传递给 obj 参数。

        如果没有成功,则不带参数调用一个方法。

        还不成功,则使用类属性。

    最后,值得注意的是,这个例子中的 items() 使用 obj 参数。 对于 items 的算法就如同上面第一步所描述的那样,首先尝试 items(obj) , 然后是 items() ,最后是 items 类属性(必须是一个列表)。

Feed 类所有方法和属性的完整文档,请参考官方的Django文档 (http://www.djangoproject.com/documentation/0.96/syndication_feeds/) 。
指定Feed的类型

默认情况下, 聚合框架生成RSS 2.0. 要改变这样的情况, 在 Feed 类中添加一个 feed_type 属性. To change that, add a feed_type attribute to your Feed class:

from django.utils.feedgenerator import Atom1Feed

class MyFeed(Feed):
  feed_type = Atom1Feed

注意你把 feed_type 赋值成一个类对象,而不是类实例。 目前合法的Feed类型如表所示。

用Python的Django框架来制作一个RSS阅读器

闭包

为了指定闭包(例如,与feed项比方说MP3 feeds相关联的媒体资源信息),使用 item_enclosure_url , item_enclosure_length , 以及 item_enclosure_mime_type ,比如

from myproject.models import Song

class MyFeedWithEnclosures(Feed):
  title = "Example feed with enclosures"
  link = "/feeds/example-with-enclosures/"

  def items(self):
    return Song.objects.all()[:30]

  def item_enclosure_url(self, item):
    return item.song_url

  def item_enclosure_length(self, item):
    return item.song_length

  item_enclosure_mime_type = "audio/mpeg"

当然,你首先要创建一个包含有 song_url 和 song_length (比如按照字节计算的长度)域的 Song 对象。
语言

聚合框架自动创建的Feed包含适当的 <language> 标签(RSS 2.0) 或 xml:lang 属性(Atom). 他直接来自于您的 LANGUAGE_CODE 设置. This comes directly from your LANGUAGE_CODE setting.
URLs

link 方法/属性可以以绝对URL的形式(例如, "/blog/" )或者指定协议和域名的URL的形式返回(例如 "http://www.example.com/blog/" )。如果 link 没有返回域名,聚合框架会根据 SITE_ID 设置,自动的插入当前站点的域信息。 (See Chapter 16 for more on SITE_ID and the sites framework.)

Atom feeds需要 <link rel="self"> 指明feeds现在的位置。 The syndication framework populates this automatically.
同时发布Atom and RSS

一些开发人员想 同时 支持Atom和RSS。 这在Django中很容易实现: 只需创建一个你的 feed 类的子类,然后修改 feed_type ,并且更新URLconf内容。 下面是一个完整的例子: Here's a full example:

from django.contrib.syndication.feeds import Feed
from django.utils.feedgenerator import Atom1Feed
from mysite.blog.models import Entry

class RssLatestEntries(Feed):
  title = "My Blog"
  link = "/archive/"
  description = "The latest news about stuff."

  def items(self):
    return Entry.objects.order_by('-pub_date')[:5]

class AtomLatestEntries(RssLatestEntries):
  feed_type = Atom1Feed

这是与之相对应那个的URLconf:

from django.conf.urls.defaults import *
from myproject.feeds import RssLatestEntries, AtomLatestEntries

feeds = {
  'rss': RssLatestEntries,
  'atom': AtomLatestEntries,
}

urlpatterns = patterns('',
  # ...
  (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
    {'feed_dict': feeds}),
  # ...
)

Python 相关文章推荐
Python简单计算文件夹大小的方法
Jul 14 Python
Python入门_浅谈for循环、while循环
May 16 Python
详解python中list的使用
Mar 15 Python
python射线法判断检测点是否位于区域外接矩形内
Jun 28 Python
Python列表的切片实例讲解
Aug 20 Python
docker django无法访问redis容器的解决方法
Aug 21 Python
Python操作qml对象过程详解
Sep 26 Python
python encrypt 实现AES加密的实例详解
Feb 20 Python
Python打包工具PyInstaller的安装与pycharm配置支持PyInstaller详细方法
Feb 27 Python
python中使用paramiko模块并实现远程连接服务器执行上传下载功能
Feb 29 Python
python破解同事的压缩包密码
Oct 14 Python
python ConfigParser库的使用及遇到的坑
Feb 12 Python
利用Python的Django框架生成PDF文件的教程
Jul 22 #Python
在Python的Django框架中生成CSV文件的方法
Jul 22 #Python
在主机商的共享服务器上部署Django站点的方法
Jul 22 #Python
在Lighttpd服务器中运行Django应用的方法
Jul 22 #Python
简单的Apache+FastCGI+Django配置指南
Jul 22 #Python
使用FastCGI部署Python的Django应用的教程
Jul 22 #Python
使用相同的Apache实例来运行Django和Media文件
Jul 22 #Python
You might like
PHP以及MYSQL日期比较方法
2012/11/29 PHP
PHP获取音频文件的相关信息
2015/06/22 PHP
浅谈PHP eval()函数定义和用法
2016/06/21 PHP
PHP简单实现上一页下一页功能示例
2016/09/14 PHP
php 算法之实现相对路径的实例
2017/10/17 PHP
PHP等比例压缩图片的实例代码
2018/07/26 PHP
PHP7 echo和print语句实例用法
2019/02/15 PHP
PHP反射学习入门示例
2019/06/14 PHP
PHP中遍历数组的三种常用方法实例分析
2019/06/24 PHP
php中yar框架实例用法讲解
2020/12/27 PHP
使用JS读秒使用示例
2013/09/21 Javascript
jQuery实现下拉框左右选择的简单实例
2014/02/22 Javascript
javascript 显示全局变量与隐式全局变量的区别
2017/02/09 Javascript
基于pako.js实现gzip的压缩和解压功能示例
2017/06/13 Javascript
分析JS单线程异步io回调的特性
2017/12/01 Javascript
vue-swiper的使用教程
2018/08/30 Javascript
微信小程序时间选择插件使用详解
2018/12/28 Javascript
JavaScript中.min.js和.js文件的区别讲解
2019/02/13 Javascript
Javascript模拟实现new原理解析
2020/03/03 Javascript
[01:09]模型精美,特效酷炫!TI9不朽宝藏Ⅰ鉴赏
2019/05/10 DOTA
Python中将字典转换为XML以及相关的命名空间解析
2015/10/15 Python
python实现发送邮件功能代码
2017/12/14 Python
python使用mysql的两种使用方式
2018/03/07 Python
Python 实现「食行生鲜」签到领积分功能
2018/09/26 Python
Python 画出来六维图
2019/07/26 Python
python 双循环遍历list 变量判断代码
2020/05/04 Python
CSS3使用transition属性实现过渡效果
2018/04/18 HTML / CSS
全球知名鞋履品牌授权零售商:Journeys
2016/09/17 全球购物
凯特方迪化妆品官网:Kat Von D Beauty
2016/11/15 全球购物
Hotels.com印度:酒店预订
2019/05/11 全球购物
联想印度官方网上商店:Lenovo India
2019/08/24 全球购物
愚人节活动策划方案
2014/03/11 职场文书
保护环境建议书300字
2014/05/13 职场文书
月度优秀员工获奖感言
2014/08/16 职场文书
单位租房协议范本
2014/12/03 职场文书
一篇文章带你复习java知识点
2021/06/28 Java/Android