Urlize Middleware

В ходе работы с SEO потребовалось на сайте Mojalanka выделять линками различные слова и фразы. Перелапачивать каждый раз кучу статей и описаний объектов при добавлении или удалении фразы - неблагодарное занятие. Можно что-то забыть или пропустить. Да и долго это… Следовательно нужно что-то более гибкое. Тут на помощь приходят middleware Django. Они позволяют сделать обработку уже сгенерированной страницы с целью замены определенных фраз на линки. Искомые фразы, а также страницы, на которые они должны указывать, храниться в базе. К ним, конечно имеется админка, но это не тема поста, тут все просто.

Рассмотрим middleware для расстановки линков. Тут главное ничего не поломать… Искомая фраза может находиться и в атрибуте тега, и быть уже линком или частью линка. Такие случаи надо пропускать.

В итоге у нас получился следующий код:

from django.conf import settings
from django.core.cache import cache
from django.utils.encoding import smart_unicode

from tours_control.seo_tools.functions import check_and_replace_word
from tours_control.seo_tools.models import HighlightedWord


class HighlightWordMiddleware():
    """Process response and change highlight words with styled hyperlinks"""

    def process_response(self, request, response):
        if response.status_code != 200:
            return response
        if "text/html" not in response.get("Content-Type"):
            return response
        cache_key = "seo_meta_highlight"
        query = cache.get(cache_key)
        if query is None or not query:
            query = HighlightedWord.objects.all()
            cache.set(cache_key, query, 12 * 3600)
        text = smart_unicode(response.content)
        for item in query:
            text = check_and_replace_word(
                text, item.keyword, item.url,
                strong=item.style_strong, italic=item.style_italic,
                underline=item.style_underline)
        response.content = text
        return response

Функция поиска и замены фраз на линки:

def check_and_replace_word(source, word, url, strong=False, italic=False,
                           underline=False):
    if word not in source:
        return source
    replacement = make_replacement(word, url, strong, italic, underline)
    result = source
    pos = result.find(word)
    while pos >= 0:
        part = result[0:pos]
        # check inside HEAD tag
        if "" not in part and "" not in part:
            pos = result.find(word, pos + 1)
            continue
        # check inside tag as attribute
        if part.count(u"<") > part.count(u">"):
            pos = result.find(word, pos + 1)
            continue
        # check inside  tag as hyperlink
        if (part.count(u"<a ") + part.count(u"<A ")) > \
                (part.count(u"</a>") + part.count(u"</A>")):
            pos = result.find(word, pos + 1)
            continue
        result = u"%s%s%s" % (part, replacement, result[pos + len(word):])
        pos = result.find(word, pos + len(replacement))
    return result


def make_replacement(word, url, strong=False, italic=False, underline=False):
    result = word
    if strong:
        result = u"%s" % result
    if italic:
        result = u"%s" % result
    if underline:
        result = u"%s" % result
    return u"%s" % (url, result)

Примечание.
Из недостатков можно отметить регистро-зависимость искомых фраз. При желании можно добавит кеширование выбора искомых фраз из базы.

 
comments powered by Disqus