Блог ищущего программиста

Быстрая генерация sitemap в django

sitemap.xml в djangoДелал автоматическую генерацию sitemap.xml для сайта на джанге. На PHP без фреймворков все решалось довольно просто: printf или echo, и все попадает прямо в буфер http. Python тоже имеет такую возможность, но часто фреймворки ее исключают. В принципе это правильно.

В django есть стандартный модуль sitemaps, который генерирует карту, но он очень медленный. Для примера, если у меня 15 тысяч элементов, генерация длится почти пол минуты. Естественно такой расклад мне не понравился, так как хотелось бы отдавать карту динамически, и не нагружать при этом все на столь долгий промежуток времени.

Я пробовал генерировать через шаблон, и ручной генерацией строковой переменной. Все давало примерно тот же результат. Меньше 20 секунд не получалось. А все потому что строки в питоне не изменяются. И каждый раз при конкатенации создается новый объект. Плюс в стандартном модуле используется get_absolute_url(), который также отнимает много времени. Естественно, что чем больше строка, тем больше время создания и уничтожения. В моем случае конечный sitemap.xml получался под 2 мегабайта. А направлять вывод в поток в django нельзя. Ни в доках ни в исходниках не нашел.

Был еще вариант генерировать все в статику раз в сутки. Но я по возможности стараюсь избегать планировщиков, так как о них нужно постоянно помнить. Конечно в питоне можно просто пустить параллельным потоком при запросе любого пользователя, но вариант с динамической генерацией мне нравится больше всего.

Так же можно было попробовать использовать генератор XML, но я прикинул, что вряд ли это приведет к чему-то хорошему, и оставил эту затею.

Сначала я сделал велосипед ускоренной конкатенации строк в стиле s10=s[0]+s[1]+s[2]+..+s[9], но потом набрел в исходниках джанги на модуль StringIO. Он генерирует строку из потока, в который мы можем предварительно послать много данных. Нашел в функции демо-сервера, который выдает «Hello World!». Скорость велосипеда и StringIO почти равны, где-то около 4 секунд. Это явно лучше 30 секунд стандартного модуля. Но оказалось есть его ускоренный аналог на C — cStringIO. Он входит в поставку питона, поэтому использовал его. Правда разницы с обычным не почувствовал. Видимо уже по умолчанию используется Сишный вариант. Вот тут есть сравнение разных генераторов текста.

В целом джанга мне реально нравится больше чем пхп-фреймворки, которые я хоть когда-то видел. Все очень логично и удобно. Хотя над некоторыми вещами пришлось посидеть. Да и скорость по моим ощущениям выше. Замерами пока не занимался.

Модуль sitemap:

 
# Генерация строки узла
def createNode(url, modified):
		return '<url>\
			  <loc>http://example.com/'+url+'</loc>\
			  <changefreq>weekly</changefreq>\
			  <lastmod>'+modified.strftime('%Y-%m-%d')+'</lastmod>\
			</url>\
			'
 
def sitemap(request):
 
        from StringIO import StringIO
 
	# Генерируем QuerySets (в принципе можно и список сгенерировать уже готовый, с сылками)
	qs1 = Model1.objects.filter(disabled=False)
	qs2 = Model2.objects.filter(disabled=False)
 
        stdout = StringIO()
 
	# Заголовочная часть XML
	print >>stdout, '<?xml version="1.0" encoding="utf-8"?>\
			<urlset\
			      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"\
			      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\
			      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9\
			            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">\
			            '
 
	# Генерируем сеты. Через get_absolute_url получается намного дольше, поэтому пока хардкод
        for item in qs1:
		print >>stdout, createNode('qs1/'+str(item.id), item.modified)
 
        for item in qs2:
		print >>stdout, createNode('qs2/'+str(item.id), item.modified)
 
 
	# Закрывающий тег
	print >>stdout, '</urlset>'
	xml = stdout.getvalue()
 
	# Рендерим вывод
	return HttpResponse(xml, mimetype="text/xml")
Опубликовал 22nd Октябрь 2012.
Размещено в django, Web.
.

Ранее в этой же рубрике:




Оставить комментарий или два

28 - здесь у нас SQL запросов.
0,046199 - время на генерацию страницы.