站点地图框架¶
Django 自带了一个高级站点地图生成框架来创建 sitemap XML 文件。
概况¶
站点地图是你网站上的一个 XML 文件,它告诉搜索引擎索引器你页面的变化频率,以及某些页面相对于你网站上其他页面的“重要性”。这些信息有助于搜索引擎对你的网站进行索引。
Django 站点地图框架通过让你用 Python 代码表达这些信息以自动创建这个 XML 文件。
它的工作原理很像 Django 的 联合框架。要创建一个站点地图,写一个 Sitemap
类,并将其指向你的 URLconf。
安装¶
要安装站点地图应用,请按照以下步骤进行:
- 将
'django.contrib.sitemaps'
添加到你的INSTALLED_APPS
配置中。 - 确保你的
TEMPLATES
配置中包含一个DjangoTemplates
后端,其APP_DIRS
选项设置为True
。默认有一个后端,所以你只需要改变这个配置就可以了。 - 确保你已经安装了
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
应该是一个字典,它将一个简短的章节标签(例如,blog
或 news
)映射到它的 Sitemap
类(例如,BlogSitemap
或 NewsSitemap
)。也可以映射到一个 Sitemap
类的 实例 (例如,BlogSitemap(some_var)
)。
Sitemap
类¶
Sitemap
类是一个 Python 类,在你的网站地图中代表一个条目的“部分”。例如,一个 Sitemap
类可以代表你的博客的所有条目,而另一个则可以代表你的事件日历中的所有事件。
在最简单的情况下,所有这些部分都被归纳到一个 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
注意:
changefreq
和priority
分别是用作<changefreq>
和<priority>
元素的类属性。它们可以作为函数调用,就像本例中的lastmod
一样。items()
是一个返回 sequence 或QuerySet
对象的方法。返回的对象将被传递给对应于站点地图属性的任何可调用方法(location
,lastmod
,changefreq
和priority
)。lastmod
应该返回一个datetime
。- 本例中没有
location
方法,但你可以提供该方法来指定对象的 URL。默认情况下,location()
调用每个对象的get_absolute_url()
并返回结果。
Sitemap
类参考¶
-
class
Sitemap
¶ Sitemap
类可以定义以下方法/属性。-
items
¶ 必须的。 一个返回对象的 sequence 或
QuerySet
的方法。框架并不关心它们是什么类型的对象,重要的是这些对象被传递给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.4
,1.0
。页面的默认优先级是0.5
。更多信息请参见 sitemaps.org documentation 。
-
protocol
¶ 可选的。
该属性定义了网站地图中 URL 的协议(
'http'
或'https'
)。如果没有设置,则使用请求网站地图的协议。如果站点地图是在请求之外建立的,则默认为'http'
。4.0 版后已移除: 在 Django 5.0 中,在请求上下文之外建立的网站地图的默认协议将从
’http'``改为 ``'https'
。
-
limit
¶ 可选的。
该属性定义了网站地图每一页所包含的 URL 的最大数量。其值不应超过默认值
50000
,这是 Sitemaps protocol 中允许的上限。
-
alternates
¶ 可选的。
一个布尔属性。当与
i18n
结合使用时,生成的 URL 将有一个备用链接列表,使用 hreflang attribute 指向其他语言版本。默认为False
。
-
x_default
¶ 可选的。
一个布尔属性。当
True
时,由alternates
生成的备用链接将包含一个hreflang="x-default"
回退条目,其值为LANGUAGE_CODE
。默认值是False
。
-
get_latest_lastmod
()¶ - New in Django 4.1.
可选项。 一个方法,返回由
lastmod
返回的最新值。此函数用于向 Sitemap 索引上下文变量 添加lastmod
属性。默认情况下,
get_latest_lastmod()
返回:- 如果
lastmod
是一个属性:lastmod
。 - 如果
lastmod
是一个方法:通过使用Sitemap.items()
返回的所有项目调用该方法后返回的最新的lastmod
。
- 如果
-
get_languages_for_item
(item)¶ - New in Django 4.2.
可选项。 一个方法,返回显示该项的语言代码序列。默认情况下,
get_languages_for_item()
返回languages
。
-
捷径¶
站点地图框架为常见的情况提供了一个方便的类:
-
class
GenericSitemap
(info_dict, priority=None, changefreq=None, protocol=None)¶ django.contrib.sitemaps.GenericSitemap
类允许您通过传递一个至少包含queryset
条目的字典来创建一个站点地图。这个查询集将用于生成站点地图的项。它还可以有一个date_field
条目,用于指定从queryset
中检索的对象的日期字段。这将在生成的站点地图中用于lastmod
属性和get_latest_lastmod()
方法。priority
、changefreq
和protocol
关键字参数允许为所有 URL 指定这些属性。
例如¶
下面是一个使用 GenericSitemap
的 URLconf 例子:
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 中使用了两个视图:
django.contrib.sitemaps.views.index()
和django.contrib.sitemaps.views.sitemap()
。 django.contrib.sitemaps.views.sitemap()
视图应该采用section
关键字作为参数。
下面是上面例子的相关 URLconf 行的样子:
from django.contrib.sitemaps import views
urlpatterns = [
path(
"sitemap.xml",
views.index,
{"sitemaps": sitemaps},
name="django.contrib.sitemaps.views.index",
),
path(
"sitemap-<section>.xml",
views.sitemap,
{"sitemaps": sitemaps},
name="django.contrib.sitemaps.views.sitemap",
),
]
这将自动生成一个 sitemap.xml
文件,同时引用 sitemap-flatpages.xml
和 sitemap-blog.xml
。Sitemap
类和 sitemaps
字典完全没有变化。
如果所有站点地图都具有由 Sitemap.get_latest_lastmod()
返回的 lastmod
,则站点地图索引将具有与最新的 lastmod
相同的 Last-Modified
头。
如果你的一个网站地图有超过 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",
),
]
已添加对 Last-Modified
头的使用。
自定义模板¶
如果你希望为网站上的每个站点地图或站点地图索引使用不同的模板,你可以通过 URLconf 向 sitemap
和 index
视图传递一个 template_name
参数来指定它。
from django.contrib.sitemaps import views
urlpatterns = [
path(
"custom-sitemap.xml",
views.index,
{"sitemaps": sitemaps, "template_name": "custom_sitemap.html"},
name="django.contrib.sitemaps.views.index",
),
path(
"custom-sitemap-<section>.xml",
views.sitemap,
{"sitemaps": sitemaps, "template_name": "custom_sitemap.html"},
name="django.contrib.sitemaps.views.sitemap",
),
]
这些视图返回 TemplateResponse
实例,允许你在渲染前轻松定制响应数据。更多细节,请看 TemplateResponse 文档。
索引¶
变量 sitemaps
是一个包含每个站点地图的 location
和 lastmod
属性的对象列表。每个 URL 具有以下属性:
location
:站点地图的位置(URL 和页面)。lastmod
:由每个站点地图的get_latest_lastmod()
方法填充。
上下文已更改为一个包含 location
和可选的 lastmod
属性的对象列表。
Sitemap¶
变量 urlset
是一个应该出现在站点地图中的 URL 列表,每个 URL 都会暴露出 Sitemap
类中定义的属性。
alternates
changefreq
item
lastmod
location
priority
当 i18n
和 alternates
被启用时,alternates
属性可用。它是一个其他语言版本的列表,包括可选的 x_default
回退,对于每个 URL。每个候补是一个字典,有 location
和 lang_code
键。
为每个 URL 添加了 item
属性,以便更灵活地定制模板,如 Google news sitemaps 。假设 Sitemap 的 items()
会返回一个带有 publication_data
和 tags
字段的项目列表,类似这样就可以生成一个 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 中进行反向查找,查找名为'django.contrib.sitemaps.views.index'
的 URL,然后是 ``'django.contrib.sitemaps.views.sitemap'``(无需其他参数)来自动确定网站地图 URL。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()
命令只有在你在 Google Search Console 注册了你的网站时才有效。
调用 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()
时引入网络开销。