您的位置:首页 > 编程语言 > Python开发

python web多sitemap创建更新解决方案

2016-08-21 20:05 363 查看

Python web多sitemap创建更新解决方案

这篇博客主要介绍本人对于Python web(这里使用的是Flask)使用多sitemap(即因为网站存在大量平均增长的页面)的解决思路与方案:

两种sitemap自动创建更新解决方案对比

方案详解

两种sitemap自动创建更新解决方案对比

方案一: 使用路由视图和模板语言自动生成

一般来说,使用python进行web开发,最简单的sitemap解决方法就是使用模板语言来创建sitemap.xml文件, 也就是的动态创建sitemap,这种方法的基本思路就是:

搜索引擎每次通过url访问该网站的sitemap文件的时候,通过路由从数据库中查询获取数据,然后填充到sitemap模板并返回.

这样做的好处以下几点:

1.除开网站的基本页面(如首页,登录页)以外,其余由由用户创建的网页(如博客,帖子)的sitemap的更新都不用管理员去手动或这调用任何命令去更新,因为搜索引擎每次访问的时候,就会自动返回从数据库中获取的最新的数据

2.简单方便,只需要写一个路由接口(视图)然后给出一个sitemap模板,就可以了.

一段Flask官网的自动生成sitemap的代码片段

这种方法虽然最简单方便,但是缺点也显而易见:

由于单个sitemap文件中包含的url上限不能超过50000个,所以这种方法只适合用户基数以及网站页面不多的小型网站, 因为小型网站一般只有一个sitemap文件。

所以说这种方法不适用于需要多个sitemap文件的页面较多的网站.

方案二:运用python的xml模块进行sitemap文件的创建与更新

这种方案,也是本人目前这里运用并将要详细介绍的方案。

这种解决方案,基本思路就是写一个集中控制sitemap文件创建和更新的方法,然后可将其作为命令调用,并设置为服务器定时任务对sitemap进行定时更新,或提供后台管理进行调用。

那么很明显,对比上面的方案一,这两种方案的最大区别就在于:

除开网站基本页面的更新(如首页,登陆等)需要人为去修改以外,对于其余的平均增长的页面(如帖子,博客等),这种方案需要管理员或linux定时任务来调用更新sitemap的方法,而上面的方案一,则完全依赖于搜索引擎那边何时访问(更新)本网站的sitemap

这种方案虽然比上面的方案一要复杂很多,但是好处在于:

适用于用户基数和页面较多的网站,因为这种方法可以管理多个sitemap的创建和更新,这样就解决了单个sitemap中url上限的问题

由于是纯粹的靠python的xml模块来进行对sitemap文件的创建和更新,也就纯粹的对文件的读写操作,所以对sitemap文件的可控性更强

比如,一个sitemap文件的内容更新后,需要根据这个sitemap文件更新的内容去更新另一个sitemap文件中的内容。

方案详解

**下面将详细讲解方案二的运用实例,这里我们主要使用的是python xml模块中的etree模块**

python xml.etree模块官方文档

注意:

etree模块对于读取非信任渠道的数据是有安全隐患的,如读取用户和前端传过来的数据,但这里我们用于操作自己网站的sitemap文件,所以这里不计较这个安全性问题。

首先说明一下这里的应用情景:这里我们有一个发帖的网站,我们要在sitemap文件中录入每一篇帖子,并且有新帖子发布的时候,要更新sitemap中帖子广场页面的url。

Sitemap文件列表:

sitemap文件名作用
sitemap_index.xmlsitemap索引文件,索引多个sitemap文件,向搜索引擎提交的就是这个文件
s_posts_(n).xml帖子页面的sitemap文件,n表示版本号,下面将详细介绍这个版本号的作用
s_base.xml基本页面的sitemap文件,如首页,登陆页,帖子广场页面等,此sitemap文件大多数时候是需要人为写入和修改的。

每个用途的sitemap文件大体内容结构:

sitemap_index.xml

<?xml version='1.0' encoding='utf-8'?>
<sitemapindex>
<sitemap>
<loc></loc>
<lastmod></lastmod>
</sitemap>
</sitemapindex>


s_posts.xml

<?xml version='1.0' encoding='utf-8'?>
<urlset>
<url>
<loc></loc>
<changefreq></changefreq>
<priority></priority>
<lastmod></lastmod>
</url>
</urlset>


s_base.xml

<?xml version='1.0' encoding='utf-8'?>
<urlset>
<!-- 这里主要是放我们的帖子广场的url -->
<url>
<loc>http://www.xfc.com/posts</loc>
<changefreq></changefreq>
<priority></priority>
<lastmod></lastmod>
</url>
</urlset>


此方案更新sitemap文件的大体流程如下:

Created with Raphaël 2.1.0调用更新所有sitemap的方法更新帖子sitemap连带更新帖子库的sitemap最终更新sitemap索引文件中上面被更新的子sitemap文件的索引写入所有更新的内容到对应的sitemap文件

接下来进入代码详解的正题:

首先我们创建一个类,一个用于封装sitemap文件的信息和操作的类,这是我们整个方案的核心之一:

from xml.etree import ElementTree as ET
import os
from flask import url_for
import ModelPost
from datetime import datetime

SITEMAP_DIR = os.path.join(os.path.abspath(os.path.dirname(__name__)), "xfc/static/sitemaps")
SITEMAP_URL = "http://wwww.xfc.cn/static/sitemaps/"

INDEX_SITEMAP_NAME = "sitemap_index"
POSTS_SITEMAP_NAME = 's_posts'
BASE_SITEMAP_NAME = "s_base"

lastmod_format = "%Y-%m-%dT%H:%M:%S+00:00"

# sitemap更新状态
NO_UPDATE = 0
MODIFY = 1
NEW_UPDATE = 2

# 每个sitemap文件最大url数量
URL_MAX_COUNT = 50000

# 默认模型帖子url数值
DEF_POST_PRIORITY = "0.6"
DEF_POST_CHANGEFREQ = "Weekly"


class SitemapFile:
"""
针对sitemap文件的获取, 创建和写入
"""
def __init__(self, base_name, sitemap_dir=SITEMAP_DIR, index=False):
"""
获取对应sitemap文件信息并赋值给实例
:param base_name: sitemap文件的基本名称(不包含后缀和版本)
:param sitemap_dir: 放置sitemap文件的目录
:param index: 是否为sitemap索引文件
"""
self.sitemap_dir = sitemap_dir
if not os.path.exists(self.sitemap_dir):
os.makedirs(self.sitemap_dir)
# 是否为索引文件
if index:
xml_item = self.get_index(base_name)
else:
xml_item = self.get_child_sitemap(base_name)
  # 赋给实例对应sitemap文件的属性值,这样最终得到SitemapFile就封装了对应sitemap文件的属性和操作,方便我们后续的操作
self.base_name = base_name
self.xml_name = xml_item.get("xml_name")
self.xml_path = xml_item.get("xml_path")
self.xml_tree = xml_item.get("xml_tree")
self.index = index
self.update = NO_UPDATE
self.xml_item = xml_item

def get_index(self, base_name="sitemap_index"):
"""获取sitemap索引文件"""
pass

def get_child_sitemap(self, base_name, auto_change=False):
"""获取普通的sitemap文件(被索引的子sitemap文件)"""
pass

def get_sitemap_item(self, base_name, stmp_version=0):
"""获取sitemap文件的信息"""
pass

def write(self):
"""将更新的内容写入到对应的sitemap文件"""
pass


首先我们注意 get_child_sitemap和get_sitemap_item这个两个方法:

get_child_sitemap就是用于获取被索引的子sitemap文件,也就是获取s_posts.xml和s_base.xml这类的sitemap文件

def get_child_sitemap(self, base_name, auto_change=False):
"""
:param auto_change 是否检测对应的sitemap文件的url数量超过上限与否,如果超过上线则获取更高的一个版本的sitemap,直至最终获取到可用的sitemap文件
"""
version = 0
# 这里调用该方法获取到对应的sitemap文件信息键值对
xml_item = self.get_sitemap_item(base_name)
if xml_item and auto_change:
# 运用while循环来获取最终可用的sitemap文件
while not check_url_count(xml_item["xml_tree"]):
version += 1
# 通过调用get_sitemap_item来获取对应的sitemap的文件信息建值对
xml_item = self.get_sitemap_item(base_name, stmp_version=version)
if not xml_item:
break

return xml_item


get_sitemap_item

def get_sitemap_item(self, base_name, stmp_version=0):
# 通过base_name加上版本号加上后缀获取到文件名
filename = "_".join([base_name, str(stmp_version)]) + ".xml"
# 这里的sitemap_dir即默认存放sitemap文件的位置
xml_path = os.path.join(self.sitemap_dir, filename)
# 检测sitemap文件是否存在,如果不存在则创建
if not os.path.exists(xml_path):
# 通过字符串创建一个Elememnt对象
xml_str = "<urlset></urlset>"
root = ET.fromstring(xml_str)
# 再将这个Element对象转换为一个ElementTree对象
tree = ET.ElementTree(root)
try:
# 最终通过ElementTree对象将内容写入到指定路径中,这样就创建了一个有内容的xml文件
# 此处的xml_declaration和encoding="utf-8"表示在xml文件内容开头添加一行声明<?xml version="1.0" encoding="utf-8" ?>
tree.write(xml_path, xml_declaration=True, encoding="utf-8")
except Exception as e:
print("获取%s的sitemap文件失败: %s" % (base_name, e))
raise e
else:
# 通过路径解析指定的xml文件,并返回一个ElementTree对象
tree = ET.parse(xml_path)
# 返回sitemap文件名,对应的ElementTree对象(对sitemap文件内容本身的一切操作主要通过这个对象)以及sitemap文件路径
return {"xml_name": filename,
"xml_tree": tree,
"xml_path": xml_path}


这里需要注意两个概念:

base_name: 表示某个用途的sitemap文件的基础名,如用于录入帖子页面的sitemap文件的基础名就是 s_posts

version或stmp_version: 这个参数表示版本号。上面说过我们的这个解决方案主要为适用于对多个sitemap文件进行创建更新,如录入帖子页面的sitemap文件,当第一个文件: s_posts_0.xml (s_posts是基础名, 0是版本号)中的url达到上限,也就是录入的帖子页面超过50000了(50000为规定的最大上限),那么我们将会将剩余和以后的帖子页面写入到更高一个版本的sitemap文件中: s_posts_1.xml,以此类推,这就保证了我们帖子页面都能有效地进行sitemap的录入

那么获取sitemap索引文件也大致相同,只不过不需要考虑版本号的问题,因为要是索引文件超出最大限制显然是不和常理的:

def get_index(self, base_name="sitemap_index"):
"""
获取sitemap索引文件
:return 返回sitemap文件信息建值对
"""
filename = base_name + ".xml"
index_file = os.path.join(self.sitemap_dir, filename)
try:
tree = ET.parse(index_file)
except Exception as e:
print("获取sitemap索引文件失败: %s" % e)
raise e
else:
return {"xml_tree": tree, "xml_path": index_file, "xml_name": filename}


好的,那么接下将就是我们这里至关重要的一步,那就是将更新和修改的内容写入到对应的sitemap文件中:

def write(self):
"""
这个方法会将修改后的sitemap内容通过对应的ElementTree对象写入到对应的sitemap文件中,但在这之前会对内容进行检测和迁移到合适版本的sitemap文件中
"""
# 先装入当前版本号的sitemap文件名
update_stmp = [self.xml_name]
# 通过check_url_count方法检测当前实例对应的sitemap文件中的url是否超过最大上限
over_max = not check_url_count(self.xml_tree)
# 没有超过最大数量的情况下直接写入当前修改内容到对应的sitemap文件
if not over_max:
try:
self.xml_tree.write(self.xml_path, xml_declaration=True, encoding="utf-8")
except Exception as e:
print("写入文件失败: %s" % e)
raise e
else:
# 返回被更新的sitemap文件名列表
return update_stmp
# 超出最大数量的情况下,我们就要将超出的url从当前版本的sitemap文件迁移到更高一个版本的sitemap文件,这也是整个多sitemap文件管理的核心动作
# 运用while循环以此类推
# 首先从第一个版本开始
version = 0
old_tree = self.xml_tree
while over_max:
version += 1
# 首先获取sitemap文件中所有的url节点对象,也就是已被录入的所有页面
# 获取到超出最大上限数量的url节点对象
urls = old_tree.findall("url")
over_urls = urls[URL_MAX_COUNT:]
if not over_urls:
break
# 然后我们通过字符串创建一个新的ElementTree对象
new_tree = ET.ElementTree(ET.fromstring("<urlset></urlset>"))
urlset = new_tree.getroot()
for url in over_urls:
# 这里要将当前版本的sitemap文件中超出的url节点移动到更高一个版本的sitemap文件中
# 这里的ET.SubElement创建了一个urlset的子元素对象
# 通过clone_url方法将超出的url节点信息克隆到新的url中
# 这里不能直接将超出的url直接设置为更高版本的sitemap文件的url,否则会报错
# 所以只能通过复制信息的方式来达到移动的目的
clone_url(ET.SubElement(urlset, "url"), url)
try:
# 再将超出的url节点从已超出容量的sitemap文件(也就是当前版本的sitemap文件)中移除
# 这样就完成了迁移url节点这个动作
old_tree.getroot().remove(url)
except:
continue
# 最后获取当前版本的文明和路径
# 为什么这里不直接调用self.xml_name和self.xml_path呢,因为我们这里要根据当前版本动态获取文件名和路径
old_tree_fn = "_".join([self.base_name, str(version-1)]) + ".xml"
old_tree_path = os.path.join(self.sitemap_dir, old_tree_fn)
tree_fn = "_".join([self.base_name, str(version)]) + ".xml"
tree_path = os.path.join(self.sitemap_dir, tree_fn)
try:
# 最终将当前版本和更高一个版本的sitemap文件的修改内容分别写入到对应的sitemap文件中
old_tree.write(old_tree_path, xml_declaration=True, encoding="utf-8")
new_tree.write(tree_path, xml_declaration=True, encoding="utf-8")
except Exception as e:
print("写入sitemap文件失败")
raise e
# 将被修改的sitemap文件名装入这个数组中
update_stmp.append(tree_fn)
# 再次检测新写入的sitemap文件是否还是也超过了最大上限,并准备下一次循环
old_tree = new_tree
over_max = not check_url_count(old_tree)
# 返回被更新的sitemap文件名列表
return update_stmp


这个方法至关重要,是整个sitemap文件解决方案的核心动作,主要运用值交换和循环检测的手段来达到保证每个sitemap中的url不超过上限。

我们先不会管哪些sitemap文件可用,哪些sitemap文件已超出上限,而是将所有的修改和更新操作都建立在第一个版本的sitemap文件对应的ElementTree对象上,然后在最后将要内容写入到文件中时,来进行内容的检测和迁移。

为避免读者对sitemap文件本身和SitemapFile对象和ElementTree对象之间关系的理解混乱,这里给出三者的关系:

Created with Raphaël 2.1.0sitemap文件解析xml文件得到ElementTree对象封装对ElementTree对象的操作SitemapFile对象

这里给出上面调用的check_url_count和clone_url方法:

def check_url_count(xml, max_count=URL_MAX_COUNT):
"""
检测sitemap中url的数量是否超过上限数量(50000)
:param max_count: 单个sitemap的url最大数量
:param xml:
:return Boolean: 该sitemap文件是否还可以填入新的url
"""
if isinstance(xml, ET.ElementTree):
tree = xml
else:
tree = ET.parse(xml)
root = tree.getroot()
url_doms = root.findall("url")
if len(url_doms) >= max_count:
return False
return True

def clone_url(new_url, old_url):
"""
克隆url中的元素数据为一个新的url
:param new_url:
:param old_url: 被克隆的url
:return: new_url
"""
for child in old_url:
new_child = ET.SubElement(new_url, child.tag)
new_child.text = child.text
return new_url


 接下来我们将介绍帖子页面和帖子广场页面的sitemap具体更新操作:

def update_post_sitemap(stmp_file):
"""
集中检测模型帖子的sitemap的更新, 并进相应操作
:params stmp_file: SitemapFile对象,封装了帖子页面的sitemap文件的属性和操作
"""
cur_tree = stmp_file.xml_tree
# 获取sitemap文件中所有的url节点元素
url_doms = cur_tree.iter("url")
update = stmp_file.update
post_items = []
post_ids = []
# 解析出sitemap <loc>元素中的post id
for url_dom in url_doms:
# 获取url节点元素中的loc子元素的值,也就是帖子页面的url
loc_dom = url_dom.find("loc")
url_items = loc_dom.text.split("/")
# 在url中,我们拆分出帖子的id
post_id = url_items[len(url_items) - 2]
# 将每个已录入到sitemap中的帖子的id和对应的url节点元素装入post_item中
post_item = {"post_id": post_id, "url_dom": url_dom}
post_items.append(post_item)
# 单独装入每个帖子的id
post_ids.append(post_id)

# 查询出没有记录到sitemap的post
unsmp_posts = ModelPost.query.filter(ModelPost.id.notin_(post_ids)).all()

print("已录入的模型帖子页面的数量: %s" % len(post_items))
print("需要录入的模型帖子页面的数量: %s" % len(unsmp_posts))
update_count = 0
delete_count = 0

# 检测已记录的post是否需要更新sitemap,有则更新sitemap
if post_items:
for post_item in post_items:
# 获取每个url节点中的lastmod子节点的值,也就是已录入到sitemap中的帖子的最后一次修改时间
insmp_lastmod = post_item["url_dom"].find("lastmod").text
post = ModelPost.query.filter_by(id=post_item["post_id"]).first()
if post:
# 这里我们对比数据库中的帖子的最后一次修改时间和已录入到sitemap中的帖子的url的lastmod
if post.edit_time.strftime(lastmod_format) != insmp_lastmod:
# 如果数据库中的帖子修改时间已更新,那么我们就要更新sitemap中的lastmod
# 通过调用udpate_post_url来修改对应post的url节点元素
cur_tree = update_post_url(post, cur_tree, modify=True)
# 这里记录被操作的sitemap文件的更新的状态
if not update:
update = MODIFY
update_count += 1
else:
# 数据库中没有查询到该帖子,那么则说明该帖子已被删除,则我们要将这条无效的帖子页面从sitemap中移除
cur_tree = delete_url_dom(cur_tree, post_item["url_dom"].find("loc").text)
if not update:
update = MODIFY
delete_count += 1

print("需要更新的模型帖子页面数量: %s" % update_count)
print("需要删除的模型帖子页面数量: %s" % delete_count)

# 将未记录到sitemap的帖子录入到sitemap中
if unsmp_posts:
for post in unsmp_posts:
# 通过调用udpate_post_url来新增url到sitemap中
cur_tree = update_post_url(post, cur_tree)
# 由于这里是添加新的url,所以这里更新udpate状态为NEW_UPDATE
if update <= MODIFY:
update = NEW_UPDATE
# 这里我们更新SitemapFile对象中的xml_tree(ElementTree实例)
stmp_file.xml_tree = cur_tree
stmp_file.update = update
return stmp_file


这里最需要说明的一点就是关于ElementTree元素:

尽量保证一个sitemap文件只被一个ElementTree对象操作,因为通过同一个sitemap文件解析出来的ElementTree对象在调用write方法写入修改的内容到sitemap文件中时,最后一个调用write方法的ElementTree对象会覆盖掉前面调用write方法的ElementTree对象的写入的内容。所以这里我们将所有的内容的修改都最终传入到一个ElementTree对象上,并最后只让这个ElementTree对象调用write方法

下面给出上面所调用的update_post_url方法(这里不再详细讲解步骤或以后更新):

def update_post_url(post, xml, dom_dict=dict(), modify=False):
"""
对sitemap中单个帖子的url的更新操作
:param xml: 文件路径或ElementTree实例
:param dom_dict: url中的元素名称和值
:param modify: 修改url or 添加url
:return tree: 返回ElementTree
"""
dom_dict.setdefault("priority", DEF_POST_PRIORITY)
dom_dict.setdefault("changefreq", DEF_POST_CHANGEFREQ)
dom_dict["lastmod"] = post.edit_time.strftime("%Y-%m-%dT%H:%M:%S+00:00")
post_url = url_for("forum.post_page", post_id=post.id, _external=True)
dom_dict["loc"] = post_url
if modify:
tree = modify_url_dom(xml, dom_dict)
else:
tree = insert_url_dom(xml, dom_dict)

return tree

def update_product_url(product_id, dom_dict):
pass

def insert_url_dom(xml, dom_dict):
"""
添加url到制定的sitemap中
:param dom_dict: 需要添加的元素的名称和值
:param xml: ElementTree实例或者xml文件路径
:return tree: 返回被更新的ElementTree实例
"""
if isinstance(xml, ET.ElementTree):
tree = xml
else:
tree = ET.parse(xml)
root = tree.getroot()
url_dom = ET.SubElement(root, "url")
for dom_name in dom_dict.keys():
the_dom = ET.SubElement(url_dom, dom_name)
the_dom.text = dom_dict.get(dom_name)

return tree

def modify_url_dom(xml, dom_dict):
"""
修改sitemap中指定的url
:param dom_dict: 需要更新的元素的名称和值
:param xml: ElementTree实例或者xml文件路径
:return tree: 返回一个ElementTree实例
"""
# 查找要更新的url, 并更新url中的元素的值
if isinstance(xml, ET.ElementTree):
tree = xml
else:
tree = ET.parse(xml)
root = tree.getroot()
url_doms = root.iter("url")
# 一般不修改loc
loc_text = dom_dict.pop("loc")

for url_dom in url_doms:
if url_dom.find("loc").text == loc_text:
for dom_name in dom_dict.keys():
the_dom = url_dom.find(dom_name)
if the_dom is not None:
if the_dom.text != dom_dict.get(dom_name):
the_dom.text = dom_dict.get(dom_name)
else:
the_dom = ET.SubElement(url_dom, dom_name)
the_dom.text = dom_dict.get(dom_name)
# 顺便填充lastmod
lastmod_dom = url_dom.find("lastmod")
if not lastmod_dom.text:
lastmod_dom.text = datetime.utcnow().strftime(lastmod_format)
return tree

def delete_url_dom(xml, loc_text):
"""
删除掉sitemap中指定的url
:param loc_text: 通过loc查找到要删除的url
:param xml: ElementTree实例或者xml文件路径
:return tree: 返回被更新的ElementTree实例
"""
if isinstance(xml, ET.ElementTree):
tree = xml
else:
tree = ET.parse(xml)
root = tree.getroot()
url_doms = root.iter("url")

for url_dom in url_doms:
if url_dom.find("loc").text == loc_text:
root.remove(url_dom)

return tree


对于上面的python.xml.etree模块的操作的困惑,请查阅官方文档或百度

python xml.etree模块官方文档

接下来就是最后的一步也是最开始的一步

集中更新网站所有的sitemap

def update_sitemap():
"""
集中更新网站所有sitemap
:return Boolean: 更新成功与否
"""
# 这个数组会装入已更新的ElementTree对象,以便将这些ElementTree对象的写入放在最后一起执行
update_smtps = []
# 通过基础名实例化获取封装了对应的sitemap文件操作的SitemapFile对象
s_posts = SitemapFile(POSTS_SITEMAP_NAME)
s_base = SitemapFile(BASE_SITEMAP_NAME)
s_index = SitemapFile(INDEX_SITEMAP_NAME, index=True)
try:
# 对帖子进行sitemap检测并更新操作
s_posts = update_post_sitemap(s_posts)
update_smtps.append(s_posts)
# 模型帖子的sitemap更新,意味着帖子库页面也要更新
if s_posts.update >= NEW_UPDATE:
# 通过调用udpat_models_sitemap方法来更新s_base.xml中的帖子库页面的url节点元素
s_base = update_models_sitemap(s_base)
update_smtps.append(s_base)

# 将所有已ElementTree实例写入到对应的sitemap文件中
write_count = 0
updated_stmps = []
# 这里我们遍历出每个更新过的ElementTree对象
for update_stmp in update_smtps:
# 根据update属性的值判断ElementTree的更新状态
if update_stmp.update >= MODIFY:
# 将每个子sitemap的更新, 写入到对应的子sitemap.xml
# 这里返回被更新的sitemap的文件名
updated_stmps = update_stmp.write()
# 更新sitemap索引文件
if updated_stmps:
write_count += len(updated_stmp)
# 通过调用udpate_index_sitemap方法并传入被更新的siteamp的文件名来更新sitemap索引文件中对应的子sitemap文件(这里主要是更新lastmod的值)
for xml_name in updated_stmps:
s_index = update_index_sitemap(s_index, child_xml_name=xml_name)
# 写入sitemap索引文件
if s_index.update >= MODIFY:
s_index.write()
write_count += 1
print("更新了sitemap索引文件")
# 反馈更新sitemap的情况信息
except Exception as e:
print("更新sitemap失败: %s" % e)
print("未对sitemap任何进行修改")
return False
else:
if write_count > 0:
print("已成功更新并写入到%s个sitemap文件" % write_count)
else:
print("无需任何更新修改")
return True


给出上面调用的更新sitemap索引文件和更新帖子广场页面的sitemap的方法:

def update_index_sitemap(stmp_file, child_xml_name):
"""
更新sitemap索引文件(子sitemap文件修改后,需要更新索引sitemap)
:param child_xml_name: 子sitemap的文件名
:param stmp_file: SitemapFile实例
"""
cur_tree = stmp_file.xml_tree
root = cur_tree.getroot()
s_doms = root.findall("sitemap")
update = NO_UPDATE

for s_dom in s_doms:
# 通过每个sitemap节点元素中的loc子节点元素的值来对比child_xml_name(子sitemap的文件名)的值查找需要更新的sitemap元素节点
loc_items = s_dom.find("loc").text.split("/")
if loc_items[len(loc_items) - 1] == child_xml_name:
# 修改lastmod为当前时间
s_dom.find("lastmod").text = datetime.utcnow().strftime(lastmod_format)
update = MODIFY

# 如果当前sitemap索引文件中没有改子sitemap的索引, 则创建
if update <= NO_UPDATE:
s_dom = ET.SubElement(root, "sitemap")
loc = ET.SubElement(s_dom, "loc")
lastmod = ET.SubElement(s_dom, "lastmod")
loc.text = os.path.join(SITEMAP_URL, child_xml_name)
lastmod.text = datetime.utcnow().strftime(lastmod_format)
update = NEW_UPDATE

stmp_file.update = update
stmp_file.xml_tree = cur_tree
return stmp_file

def update_models_sitemap(stmp_file):
"""
更新帖子广场页面和搜索页面(新增帖子之后,需要更新帖子广场页面和搜索页面)
这里的搜索页面的内容跟帖子广场页面基本相同
"""
cur_tree = stmp_file.xml_tree
lastmod = datetime.utcnow().strftime(lastmod_format)
models_dom = {"loc": "http://www.xfc.com/model", "lastmod": lastmod}
search_dom = {"loc": "http://www.xfc.com/search", "lastmod": lastmod}
# modify_url_dom方法上面已给出
cur_tree = modify_url_dom(cur_tree, models_dom)
cur_tree = modify_url_dom(cur_tree, search_dom)
stmp_file.xml_tree = cur_tree
stmp_file.update = MODIFY
return stmp_file


这两个方法都比较简单,无非就是操作ElemenTree对象来进行修改和更新。

总结

上面已经给出整个方案的全部代码,大多数逻辑上的讲解都在上面代码的注释当中,目前整个方案还有不少的优化和调整的空间,目前的话,读者就当参考参考了。

目录

Python web多sitemap创建更新解决方案

两种sitemap自动创建更新解决方案对比
方案一 使用路由视图和模板语言自动生成

方案二运用python的xml模块进行sitemap文件的创建与更新

方案详解
Sitemap文件列表

每个用途的sitemap文件大体内容结构

此方案更新sitemap文件的大体流程如下

接下来进入代码详解的正题

 接下来我们将介绍帖子页面和帖子广场页面的sitemap具体更新操作

集中更新网站所有的sitemap

总结

目录
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐