среда, 22 сентября 2010 г.

(ИМХО) лучшее решение для чистки html от мусора


# -*- coding:utf-8 -*-

from lxml.html.clean import Cleaner
import re

class CleanEmpty(Cleaner):

    white = re.compile('[\r\n\t]+')

    def __call__(self, doc):
        super(CleanEmpty, self).__call__(doc)

        empty = []
        for el in doc.iter():
            if el.text:
                el.text = self.white.sub('', el.text.strip()).replace('  ', ' ')
            elif not list(el):
                empty.append(el)
            if el.tail:
                el.tail = ''
        for el in empty:
            el.drop_tag()

def clean_html(buf):

    c = CleanEmpty(
        allow_tags=('html','body', 'a', 'p', 'img', 'h2', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'strong', 'em', 'sup', 'sub', 'ul', 'ol', 'li'),
        scripts=False,
        style=True,
        comments=False,
        page_structure=False,
        remove_unknown_tags=False)
    return c.clean_html(buf)

if __name__ == '__main__':
    import sys
    print clean_html(open(sys.argv[1]).read().decode('utf-8'))[12:-14].encode('utf-8')

воскресенье, 31 января 2010 г.

Django: Яндекс RSS

Привет коллеги, которые пишут свои реализации RSS специально для Яндекса.
Приведенный код также добавляет в syndication возможность создавать элементы с CDATA

from django.contrib.syndication.feeds import Feed
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.feedgenerator import Rss201rev2Feed


class SXG(SimplerXMLGenerator):
    def addQuickElementCDATA(self, name, contents=None, attrs=None):
        if attrs is None: attrs = {}
        self.startElement(name, attrs)
        if contents is not None:
            self._write('<![CDATA['+contents+']]>')
        self.endElement(name)
        

class Rss(Rss201rev2Feed):
    def write(self, outfile, encoding):
        handler = SXG(outfile, encoding)
        handler.startDocument()
        handler.startElement(u"rss", self.rss_attributes())
        handler.startElement(u"channel", self.root_attributes())
        self.add_root_elements(handler)
        self.write_items(handler)
        self.endChannelElement(handler)
        handler.endElement(u"rss")
    
    def add_item_elements(self, handler, item):
        if item['description'] is not None:
            handler.addQuickElementCDATA(u'description', item['description'])
            item['description'] = None
        super(Rss, self).add_item_elements(handler, item)


class RssYandex(Rss):
    def rss_attributes(self):
        attrs = super(RssYandex, self).rss_attributes()
        attrs.update({'xmlns':'http://backend.userland.com/rss2', 'xmlns:yandex':'http://news.yandex.ru'})
        return attrs
    
    def add_item_elements(self, handler, item):
        if item['fulltext'] is not None:
            handler.addQuickElementCDATA(u'yandex:full-text', item['fulltext'])
        super(RssYandex, self).add_item_elements(handler, item)


пример использования:
class MyFeed(Feed):
    feed_type = RssYandex

    def item_extra_kwargs(self, item):
        return {'fulltext':item.body.rendered}


и не забываем про любимую Яндексом кодировку:
def feed(request, url):
    try:
        slug, param = url.split('/',1)
    except ValueError:
        slug, param = url, ''
    
    f = MyFeed(slug, request).get_feed(param)
    
    response = HttpResponse(mimetype=f.mime_type)
    f.write(response, 'windows-1251')
    return response


если вам не нужна поддержка CDATA, то можно обойтись малой кровью
from django.utils.feedgenerator import Rss201rev2Feed

class RssYandex(Rss201rev2Feed):
    def rss_attributes(self):
        attrs = super(RssYandex, self).rss_attributes()
        attrs.update({'xmlns':'http://backend.userland.com/rss2', 'xmlns:yandex':'http://news.yandex.ru'})
        return attrs
    
    def add_item_elements(self, handler, item):
        if item['fulltext'] is not None:
            handler.addQuickElement(u'yandex:full-text', item['fulltext'])
        super(RssYandex, self).add_item_elements(handler, item)