Создание большой XML-карты сайта для Django
Предположим, что у вас так много страниц (тысячи), что вы не можете просто создать один файл /sitemap.xml
, в котором перечислены все URL-адреса (он же <loc>). Поэтому вам нужно создать /sitemaps.xml
, который указывает на другие файлы карты сайта. А если адресов в каждом тысячи, то нужно сжать эти файлы.
В статье показано, как создать файл карты сайта, который указывает на 63 файла sitemap-{M}-{N}.xml.gz
, которые охватывают около 1 000 000 URL-адресов. Контекст здесь - это Python, а получение данных происходит из Django. Python - ключ к успеху, но если у вас есть что-то отличное от Django, вы можете присмотрется к самой идее и мысленно заменить его на свой собственный преобразователь данных.
Генерация .xml.gz
файлов
Вот сама суть работы. Функция генератора, принимающая экземпляр Django QuerySet (который упорядочен и фильтруется!), начинает генерировать деревья etree и выгружает их на диск с помощью gzip.
import gzip
from lxml import etree
outfile = "sitemap-{start}-{end}.xml"
batchsize = 40_000
def generate(self, qs, base_url, outfile, batchsize):
# Use `.values` to make the query much faster
qs = qs.values("name", "id", "artist_id", "language")
def start():
return etree.Element(
"urlset", xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
)
def close(root, filename):
with gzip.open(filename, "wb") as f:
f.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
f.write(etree.tostring(root, pretty_print=True))
root = filename = None
count = 0
for song in qs.iterator():
if not count % batchsize:
if filename: # not the very first loop
close(root, filename)
yield filename
filename = outfile.format(start=count, end=count + batchsize)
root = start()
loc = "{}{}".format(base_url, make_song_url(song))
etree.SubElement(etree.SubElement(root, "url"), "loc").text = loc
count += 1
close(root, filename)
yield filename
Наиболее важные строки с точки зрения lxml.etree
и sitemaps
:
root = etree.Element("urlset", xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
...
etree.SubElement(etree.SubElement(root, "url"), "loc").text = loc
Еще одна важная вещь - примечание об использовании .values()
. Если вы этого не сделаете, Django создаст экземпляр модели для каждой строки, возвращаемой итератором. Это дорого.
Еще одна важная вещь - использовать итератор Django ORM, поскольку это гораздо эффективнее, чем возиться с ограничениями и смещениями.
Генерация карты карт
Создание карты карт не нуждается в сжатии, так как она будет крошечной.
def generate_map_of_maps(base_url, outfile):
root = etree.Element(
"sitemapindex", xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
)
with open(outfile, "wb") as f:
f.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
files_created = sorted(glob("sitemap-*.xml.gz"))
for file_created in files_created:
sitemap = etree.SubElement(root, "sitemap")
uri = "{}/{}".format(base_url, os.path.basename(file_created))
etree.SubElement(sitemap, "loc").text = uri
lastmod = datetime.datetime.fromtimestamp(
os.stat(file_created).st_mtime
).strftime("%Y-%m-%d")
etree.SubElement(sitemap, "lastmod").text = lastmod
f.write(etree.tostring(root, pretty_print=True))
Вернуться на верх