用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 相关文章推荐
使用IronPython把Python脚本集成到.NET程序中的教程
Mar 31 Python
对Python新手编程过程中如何规避一些常见问题的建议
Apr 01 Python
简单介绍Python的Django框架的dj-scaffold项目
May 30 Python
python中nan与inf转为特定数字方法示例
May 11 Python
python中的json总结
Oct 11 Python
使用TensorFlow-Slim进行图像分类的实现
Dec 31 Python
pytorch 限制GPU使用效率详解(计算效率)
Jun 27 Python
如何通过安装HomeBrew来安装Python3
Dec 23 Python
Python对excel的基本操作方法
Feb 18 Python
python实现b站直播自动发送弹幕功能
Feb 20 Python
如何将numpy二维数组中的np.nan值替换为指定的值
May 14 Python
Python中文纠错的简单实现
Jul 07 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英文字母大小写转换函数小结
2014/05/03 PHP
一个非常完美的读写ini格式的PHP配置类分享
2015/02/12 PHP
PHP实现的ID混淆算法类与用法示例
2018/08/10 PHP
php如何获取Http请求
2020/04/30 PHP
top.location.href 没有权限 解决方法
2008/08/05 Javascript
Javascript中Eval函数的使用说明
2008/10/11 Javascript
js文字横向滚动特效
2015/11/11 Javascript
JavaScript 实现的checkbox经典实例分享
2016/10/16 Javascript
Bootstrap轮播图的使用和理解4
2016/12/14 Javascript
AngularJS遍历获取数组元素的方法示例
2017/11/11 Javascript
webpack v4 从dev到prd的方法
2018/04/02 Javascript
还不懂递归?读完这篇文章保证你会懂
2018/07/29 Javascript
原生js实现针对Dom节点的CRUD操作示例
2019/08/26 Javascript
ES6中let、const的区别及变量的解构赋值操作方法实例分析
2019/10/15 Javascript
vue+ESLint 配置保存 自动格式化代码
2020/03/17 Javascript
学习python 之编写简单乘法运算题
2016/02/27 Python
Scrapy的简单使用教程
2017/10/24 Python
Python批量更改文件名的实现方法
2017/10/29 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
python文本数据处理学习笔记详解
2019/06/17 Python
python多线程+代理池爬取天天基金网、股票数据过程解析
2019/08/13 Python
OpenCV图片漫画效果的实现示例
2020/08/18 Python
python中判断数字是否为质数的实例讲解
2020/12/06 Python
css3选择器基本介绍
2014/12/15 HTML / CSS
利用CSS3实现的文字定时向上滚动
2016/08/29 HTML / CSS
美国维生素、补充剂、保健食品购物网站:Vitacost
2016/08/05 全球购物
高品质和独特的产品世界:Creations and Collections
2018/01/07 全球购物
Infababy英国:婴儿推车、Travel System婴儿车和婴儿汽车座椅销售
2018/05/23 全球购物
东南亚冒险旅行与活动:Adventoro
2019/10/16 全球购物
网络方面基础面试题
2012/11/16 面试题
公交公司毕业生求职信
2014/02/15 职场文书
大学军训感言300字
2014/03/09 职场文书
《庐山的云雾》教学反思
2014/04/22 职场文书
沈阳故宫导游词
2015/01/31 职场文书
MySQL 百万级数据的4种查询优化方式
2021/06/07 MySQL
Spring Bean是如何初始化的详解
2022/03/22 Java/Android