YOLO813

django制作站点地图sitemap(2)

    上一篇文章介绍了如何利用django来制作站点地图,其实还有一个比较简单的方法,也是针对5万条以下的站点地图文件来说的,在django的官方文档中,The sitemap framework页面下有一个Shortcuts快捷方式,利用GenericSitemap类,在查阅源代码时,我发现它也是继承Sitemap,只不过在初始化时封装了一个info_dict字典,它有一个必须要传递的键queryset,其中date_field由于是调用get方法所以非必填项,另外权重等值也可以自己选填

# 来源 from django.contrib import sitemaps


# 实际代码
class GenericSitemap(Sitemap):
    priority = None
    changefreq = None

    def __init__(self, info_dict, priority=None, changefreq=None, protocol=None):
        self.queryset = info_dict['queryset']
        self.date_field = info_dict.get('date_field')
        self.priority = priority
        self.changefreq = changefreq
        self.protocol = protocol

    def items(self):
        # Make sure to return a clone; we don't want premature evaluation.
        return self.queryset.filter()

    def lastmod(self, item):
        if self.date_field is not None:
            return getattr(item, self.date_field)
        return None

可以看到这里也是不存在返回站点地图路径的get_absolute_url函数的,所以,我们必须要在模型类里面定义一个路径函数

#models.py
def get_absolute_url(self):
    return "/nba/name/"+ self.number

然后回到ROOT_URLCONF,键入如下代码:

#导入快捷方式
# urls.py
from django.contrib.sitemaps import GenericSitemap
info_dict = {
    "queryset": Nba.objects.only("id"),
    "date_field":"create_time",
}
urlpatterns = [
  path('sitemap.xml', sitemap, {'sitemaps': {'blog': GenericSitemap(info_dict, priority=0.6,changefreq="daily")}}, name='django.contrib.sitemaps.views.sitemap'),
]

这里面可能唯一有点区别的就是我使用了Nba.objects.only("id"),而官网采用的是Nba.objects.all(),在数据量不大的时候(测试环境28000条数据)两者没什么区别,可能也就是0.03秒和0.2秒的区别,但是数据量过大时,这个还是有优化的必要的,其它的内容就照着写就可以了,再次访问sitemap.xml路径时就可以看见自己的站点地图了。
    百度上有很多教程把'django.contrib.sites'安装到INSTALLED_APPS中,这个其实并非是做站点地图所必须,这是django为了在生产中为不同的站点提供服务而提供的一个配置,如果在配置文件中配置了django.contrib.sites,然后设置SITE_ID = 1,进行migrate之后,你会发现在数据库里面多了一张表,这个表里面存了一条名称为example.com的数据,你建完站点地图文件之后,如果发现前缀都变成了example.com/sitemap.xml这种,可以检查一下,是否做了如上配置,如果没有安装sites,那么默认会使用当前域名来作为host。

    重头戏来了,如何创建超过50000条url的sitemap呢?参考django的官方文档Creating a sitemap index,“如果您的一个站点地图具有超过50,000个URL,则应创建一个索引文件。在这种情况下,Django将自动对站点地图进行分页,索引将对此进行分页。”

    跟上一篇文章类似,我们新建一个sitemap.py文件

#sitemap.py
from django.contrib import sitemaps
class DynamicSitemap(sitemaps.Sitemap):
    changefreq = "weekly"
    priority = 0.6
    def items(self):
        return Nba.objects.all()
    def location(self, obj):
        return "/nba/name/"+obj.number
    def lastmod(self, obj):
        return obj.update_time

    在ROOT_URLCONF中,导入自己定义的sitemap,并加入两个路径规则

# urls.py
from django.contrib.sitemaps import views
my_sitemap = {
    'nba':DynamicSitemap,
}
urlpatterns = [
  path("nba_dynamic_sitemap.xml", views.index, {"sitemaps": my_sitemap}),
  path("nba-<section>.xml", views.sitemap, {"sitemaps": my_sitemap}, name='django.contrib.sitemaps.views.sitemap'),
]

那么,此时我们访问nba_dynamic_sitemap.xml路径就可以看见自己的站点地图了,而且一旦超过了5万条url,会自动创建分页,如下所示,由于我的数据不足5万条,所以只有一条索引文件,而且该索引命名的方式为nba-<section>,这个section的取之于my_sitemap中定义的键(上文中为nba),所以你看到这个索引的命名为nba-nba.xml。

    我顺便将其优化了一下,因为搜索引擎来抓取的时候,一次性返回50000条数据对数据库还是有些压力的,所以,让其每10000条链接则分页:

# sitemap.py
from django.contrib import sitemaps
class LimitSitemap(sitemaps.Sitemap):
    limit = 10000
class DynamicSitemap(LimitSitemap):
    changefreq = "weekly"
    ......

    新建一个LimitSitemap类,继承Sitemap类,覆写其limit属性,然后让DynamicSitemap继承自己写的LimitSitemap类就可以了,Sitemap具体还有哪些属性可以翻阅源代码读一读,后面有时间来改造的时候再来记录,如下所示,28000条数据分成了3个链接,收工!

    当然,还是有优化的空间,例如,sql聚合语句的优化,站点地图文件进行gz压缩节省带宽,又或者将站点地图文件制成静态文件直接返回,不用经过数据库等等,由于时间关系,还没有研究,后续有需要再来进行吧。