站点地图框架

Django 自带了一个高级站点地图生成框架来创建 sitemap XML 文件。

概况

站点地图是你网站上的一个 XML 文件,它告诉搜索引擎索引器你页面的变化频率,以及某些页面相对于你网站上其他页面的“重要性”。这些信息有助于搜索引擎对你的网站进行索引。

Django 站点地图框架通过让你用 Python 代码表达这些信息以自动创建这个 XML 文件。

它的工作原理很像 Django 的 联合框架。要创建一个站点地图,写一个 Sitemap 类,并将其指向你的 URLconf

安装

要安装站点地图应用,请按照以下步骤进行:

  1. 'django.contrib.sitemaps' 添加到你的 INSTALLED_APPS 配置中。
  2. 确保你的 TEMPLATES 配置中包含一个 DjangoTemplates 后端,其 APP_DIRS 选项设置为 True。默认有一个后端,所以你只需要改变这个配置就可以了。
  3. 确保你已经安装了 sites framework

(注意:站点地图应用不会安装任何数据库表。它需要进入 INSTALLED_APPS 的唯一原因是为了让 Loader() 模板加载器能够找到默认模板。)

初始化

views.sitemap(request, sitemaps, section=None, template_name='sitemap.xml', content_type='application/xml')

要在你的 Django 网站上激活站点地图的生成,在你的 URLconf 中添加这一行:

from django.contrib.sitemaps.views import sitemap

path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
     name='django.contrib.sitemaps.views.sitemap')

这告诉 Django 在客户端访问 /sitemap.xml 时建立一个站点地图。

站点地图文件的名称并不重要,但位置很重要。搜索引擎只对当前URL级别及以下的站点地图链接进行索引。例如,如果 sitemap.xml 位于你的根目录下,它可以引用您网站中的任何URL。但是,如果您的站点地图位于 /content/sitemap.xml,则只能引用以 /content/ 开头的 URL。

sitemap 视图需要一个额外的、必要的参数:{'sitemaps': sitemaps}sitemaps 应该是一个字典,它将一个简短的章节标签(例如,blognews)映射到它的 Sitemap 类(例如,BlogSitemapNewsSitemap)。也可以映射到一个 Sitemap 类的 实例 (例如,BlogSitemap(some_var))。

Sitemap

Sitemap 类是一个 Python 类,它代表了站点地图中的“部分”条目。例如,一个 Sitemap 类可以代表你的 Weblog 的所有条目,而另一个可以代表你的事件日历中的所有事件。

在最简单的情况下,所有这些部分都被归纳到一个 sitemap.xml 中,但也可以使用该框架生成一个站点地图索引来引用单个站点地图文件,每个部分一个。(参见下面的 创建一个站点地图索引 )。

Sitemap 类必须是 django.contrib.sitemaps.Sitemap 子类。它们可以存在于你代码库的任何地方。

一个例子

让我们假设你有一个博客系统,有一个 Entry 模型,你希望你的网站地图包括所有链接到你的个人博客条目。下面是你的网站地图类的样子:

from django.contrib.sitemaps import Sitemap
from blog.models import Entry

class BlogSitemap(Sitemap):
    changefreq = "never"
    priority = 0.5

    def items(self):
        return Entry.objects.filter(is_draft=False)

    def lastmod(self, obj):
        return obj.pub_date

注意:

  • changefreqpriority 分别是用作 <changefreq><priority> 元素的类属性。它们可以作为函数调用,就像本例中的 lastmod 一样。
  • items() 是一个返回 sequenceQuerySet 对象的方法。返回的对象将被传递给对应于站点地图属性的任何可调用方法(locationlastmodchangefreqpriority)。
  • lastmod 应该返回一个 datetime
  • 本例中没有 location 方法,但你可以提供该方法来指定对象的 URL。默认情况下,location() 调用每个对象的 get_absolute_url() 并返回结果。

Sitemap 类参考

class Sitemap

Sitemap 类可以定义以下方法/属性。

items

必须的。 一个返回对象的 sequenceQuerySet 的方法。框架并不关心它们是什么类型的对象,重要的是这些对象被传递给 location()lastmod()changefreq()priority() 方法。

location

可选的。 方法或属性。

如果是方法,则应返回 items() 返回的给定对象的绝对路径。

如果它是一个属性,它的值应该是一个字符串,代表一个绝对路径,用于 items() 返回的 每一个 对象。

在这两种情况下,“绝对路径”指的是不包含协议或域名的 URL。例如:

  • 对的:'/foo/bar/'
  • 错的:'example.com/foo/bar/'
  • 错的:'https://example.com/foo/bar/'

如果没有提供 location,框架将调用 items() 返回的每个对象的 get_absolute_url() 方法。

如果要指定 'http' 以外的协议,请使用 protocol

lastmod

可选的。 方法或属性。

如果它是一个方法,它应该接受一个参数——一个由 items() 返回的对象——并以 datetime 返回该对象最后修改的日期/时间。

如果是属性,其值应该是 datetime,代表 items() 返回的 每一个 对象的最后修改日期/时间。

如果站点地图中的所有项目都有一个 lastmod,那么由 views.sitemap() 生成的站点地图将有一个 Last-Modified 头,等于最新的 lastmod。你可以激活 ConditionalGetMiddleware,让 Django 对请求做出适当的响应,并提供 If-Modified-Since 头,防止发送没有变化的站点地图。

changefreq

可选的。 方法或属性。

如果它是一个方法,它应该接受一个参数——一个由 items() 返回的对象——并以字符串形式返回该对象的变化频率。

如果是属性,其值应该是一个字符串,代表 items() 返回的 每一个 对象的变化频率。

changefreq 的可能值,无论你是使用方法还是属性,都是:

  • 'always'
  • 'hourly'
  • 'daily'
  • 'weekly'
  • 'monthly'
  • 'yearly'
  • 'never'
priority

可选的。 方法或属性。

如果它是一个方法,它应该接受一个参数——一个由 items() 返回的对象——并以字符串或浮点数的形式返回该对象的优先级。

如果它是一个属性,它的值应该是一个字符串或浮点数,代表 items() 返回的 每一个 对象的优先级。

priority 的示例值:0.41.0。页面的默认优先级是 0.5。更多信息请参见 sitemaps.org documentation

protocol

可选的。

该属性定义了网站地图中 URL 的协议('http''https')。如果没有设置,则使用请求网站地图的协议。如果站点地图是在请求之外建立的,则默认为 'http'

limit

可选的。

该属性定义了网站地图每一页所包含的 URL 的最大数量。其值不应超过默认值 50000,这是 Sitemaps protocol 中允许的上限。

i18n

可选的。

一个布尔属性,用于定义该网站地图的 URL 是否应使用所有的 LANGUAGES 生成。默认值是 False

捷径

站点地图框架为常见的情况提供了一个方便的类:

class GenericSitemap(info_dict, priority=None, changefreq=None, protocol=None)

django.contrib.sitemaps.GenericSitemap 类允许你通过传递一个至少包含一个 queryset 条目的字典来创建一个站点地图。该查询结果集将用于生成站点地图的项目。它也可以有一个 date_field 条目,为从 queryset 中检索的对象指定一个日期字段。这将用于生成的站点地图中的 lastmod 属性。

prioritychangefreqprotocol 关键字参数允许为所有 URL 指定这些属性。

例如

下面是一个使用 GenericSitemapURLconf 例子:

from django.contrib.sitemaps import GenericSitemap
from django.contrib.sitemaps.views import sitemap
from django.urls import path
from blog.models import Entry

info_dict = {
    'queryset': Entry.objects.all(),
    'date_field': 'pub_date',
}

urlpatterns = [
    # some generic view using info_dict
    # ...

    # the sitemap
    path('sitemap.xml', sitemap,
         {'sitemaps': {'blog': GenericSitemap(info_dict, priority=0.6)}},
         name='django.contrib.sitemaps.views.sitemap'),
]

静态视图的站点地图

通常情况下,你希望搜索引擎爬虫能够索引那些既不是对象详情页也不是简单页面的视图。解决的办法是在 items 中明确列出这些视图的 URL 名称,并在站点地图的 location 方法中调用 reverse()。例如:

# sitemaps.py
from django.contrib import sitemaps
from django.urls import reverse

class StaticViewSitemap(sitemaps.Sitemap):
    priority = 0.5
    changefreq = 'daily'

    def items(self):
        return ['main', 'about', 'license']

    def location(self, item):
        return reverse(item)

# urls.py
from django.contrib.sitemaps.views import sitemap
from django.urls import path

from .sitemaps import StaticViewSitemap
from . import views

sitemaps = {
    'static': StaticViewSitemap,
}

urlpatterns = [
    path('', views.main, name='main'),
    path('about/', views.about, name='about'),
    path('license/', views.license, name='license'),
    # ...
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
         name='django.contrib.sitemaps.views.sitemap')
]

创建站点地图索引

views.index(request, sitemaps, template_name='sitemap_index.xml', content_type='application/xml', sitemap_url_name='django.contrib.sitemaps.views.sitemap')

站点地图框架还能够创建一个站点地图索引,该索引能够引用单独的站点地图文件,每个部分在你的 sitemaps 字典中定义一个。唯一不同的用法是:

下面是上面例子的相关 URLconf 行的样子:

from django.contrib.sitemaps import views

urlpatterns = [
    path('sitemap.xml', views.index, {'sitemaps': sitemaps}),
    path('sitemap-<section>.xml', views.sitemap, {'sitemaps': sitemaps},
         name='django.contrib.sitemaps.views.sitemap'),
]

这将自动生成一个 sitemap.xml 文件,同时引用 sitemap-flatpages.xmlsitemap-blog.xmlSitemap 类和 sitemaps 字典完全没有变化。

如果你的一个网站地图有超过 50,000 个 URL,你应该创建一个索引文件。在这种情况下,Django 会自动对网站地图进行分页,索引也会反映出来。

如果你没有使用普通的站点地图视图——例如,如果它是用缓存装饰器包装的——你必须为你的站点地图视图命名,并将 sitemap_url_name 传给索引视图:

from django.contrib.sitemaps import views as sitemaps_views
from django.views.decorators.cache import cache_page

urlpatterns = [
    path('sitemap.xml',
         cache_page(86400)(sitemaps_views.index),
         {'sitemaps': sitemaps, 'sitemap_url_name': 'sitemaps'}),
    path('sitemap-<section>.xml',
         cache_page(86400)(sitemaps_views.sitemap),
         {'sitemaps': sitemaps}, name='sitemaps'),
]

自定义模板

如果你希望为网站上的每个站点地图或站点地图索引使用不同的模板,你可以通过 URLconf 向 sitemapindex 视图传递一个 template_name 参数来指定它。

from django.contrib.sitemaps import views

urlpatterns = [
    path('custom-sitemap.xml', views.index, {
        'sitemaps': sitemaps,
        'template_name': 'custom_sitemap.html'
    }),
    path('custom-sitemap-<section>.xml', views.sitemap, {
        'sitemaps': sitemaps,
        'template_name': 'custom_sitemap.html'
    }, name='django.contrib.sitemaps.views.sitemap'),
]

这些视图返回 TemplateResponse 实例,允许你在渲染前轻松定制响应数据。更多细节,请看 TemplateResponse 文档

上下文变量

在自定义 index()sitemap() 视图的模板时,可以依赖以下上下文变量。

索引

变量 sitemaps 是指向每个站点地图的绝对 URL 的列表。

Sitemap

变量 urlset 是一个应该出现在站点地图中的 URL 列表,每个 URL 都会暴露出 Sitemap 类中定义的属性。

  • changefreq
  • item
  • lastmod
  • location
  • priority

为每个 URL 添加了 item 属性,以便更灵活地定制模板,如 Google news sitemaps 。假设 Sitemap 的 items() 会返回一个带有 publication_datatags 字段的项目列表,类似这样就可以生成一个 Google News 兼容的网站地图。

<?xml version="1.0" encoding="UTF-8"?>
<urlset
  xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
{% spaceless %}
{% for url in urlset %}
  <url>
    <loc>{{ url.location }}</loc>
    {% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
    {% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
    {% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
    <news:news>
      {% if url.item.publication_date %}<news:publication_date>{{ url.item.publication_date|date:"Y-m-d" }}</news:publication_date>{% endif %}
      {% if url.item.tags %}<news:keywords>{{ url.item.tags }}</news:keywords>{% endif %}
    </news:news>
   </url>
{% endfor %}
{% endspaceless %}
</urlset>

通知谷歌

当你的网站地图发生变化时,你可能想“ping”一下谷歌,让它知道要重新索引你的网站。站点地图框架提供了一个函数来实现这个功能: django.contrib.sitemaps.ping_google()

ping_google(sitemap_url=None, ping_url=PING_URL, sitemap_uses_https=True)

ping_google 接受这些可选的参数:

  • sitemap_url - 通往网站站点地图的绝对路径(例如: '/sitemap.xml')。如果没有提供这个参数,ping_google 将尝试通过在 URLconf 中进行反向查找来获取网站地图。
  • ping_url - 默认为谷歌的 Ping 工具:https://www.google.com/webmasters/tools/ping
  • sitemap_uses_https - 如果你的网站使用 http 而不是 https,设置为 False

ping_google() 如果不能确定你的站点地图 URL,会引发异常 django.contrib.sitemaps.SitemapNotFound

调用 ping_google() 的一个有用的方法是在模型的 save() 方法:

from django.contrib.sitemaps import ping_google

class Entry(models.Model):
    # ...
    def save(self, force_insert=False, force_update=False):
        super().save(force_insert, force_update)
        try:
            ping_google()
        except Exception:
            # Bare 'except' because we could get a variety
            # of HTTP-related exceptions.
            pass

然而,一个更有效的解决方案是在定时脚本中调用 ping_google(),或者其他一些预定任务。该函数向谷歌的服务器发出 HTTP 请求,所以你可能不想在每次调用 save() 时引入网络开销。

通过 manage.py 通知谷歌

django-admin ping_google [sitemap_url]

一旦将站点地图应用程序添加到你的项目中,你也可以使用 ping_google 管理命令通知谷歌:

python manage.py ping_google [/sitemap.xml]
--sitemap-uses-http

如果你的网站地图使用 http 而不是 https,请使用这个选项。