<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Code-Is-Art &#187; Python</title>
	<atom:link href="http://www.codeisart.ru/rubric/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.codeisart.ru</link>
	<description>Статьи по SEO, дизайну и программированию. Переводы на веб-тематику. Исследования и наработки в области интернет-технологий.</description>
	<lastBuildDate>Tue, 08 Nov 2011 13:43:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Python: Получение последнего сообщения из Twitter</title>
		<link>http://www.codeisart.ru/python-the-latest-messages-from-twitter/</link>
		<comments>http://www.codeisart.ru/python-the-latest-messages-from-twitter/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 21:13:40 +0000</pubDate>
		<dc:creator>Skaizer</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Django]]></category>

		<guid isPermaLink="false">http://www.codeisart.ru/?p=1681</guid>
		<description><![CDATA[Пишем сейчас на&#160;Django сайт, где требуется получать последнее сообщение из&#160;Twitter аккаунта корпоративного микроблога. Некоторое время назад я&#160;показывал, как это можно сделать в&#160;PHP, теперь пишу тоже самое на&#160;Python. Небольшие отличия есть, а&#160;именно&#160;&#8212; мы&#160;получаем данные из&#160;Twitter API в&#160;формате JSON (используется библиотека simplejson), а&#160;не&#160;в&#160;XML, как это было в&#160;PHP версии. Можно без проблем использовать и&#160;XML, просто с&#160;JSON мне удобнее [...]]]></description>
			<content:encoded><![CDATA[<p>Пишем сейчас на&nbsp;Django сайт, где требуется получать последнее сообщение из&nbsp;Twitter аккаунта корпоративного микроблога. Некоторое время назад я&nbsp;показывал, как <a href="http://www.codeisart.ru/php-extracting-the-last-twitter-record/">это можно сделать в&nbsp;PHP</a>, теперь пишу тоже самое на&nbsp;Python.</p>
<p><span id="more-1681"></span></p>
<p>Небольшие отличия есть, а&nbsp;именно&nbsp;&mdash; мы&nbsp;получаем данные из&nbsp;Twitter API в&nbsp;формате JSON (используется библиотека <a href="http://pypi.python.org/pypi/simplejson/">simplejson</a>), а&nbsp;не&nbsp;в&nbsp;XML, как это было в&nbsp;PHP версии. Можно без проблем использовать и&nbsp;XML, просто с&nbsp;JSON мне удобнее работать.</p>
<p>Аналогично реализовано кеширование твитта в локальный файл и обновление через определенный промежуток времени.</p>
<p>Код, вся дополнительная информация в комментариях:</p>
<pre><code class="python">from django.conf import settings
from time import time
import urllib, os, simplejson

class Twitter:
  """
  Скачиваем последнее сообщение из Twitter пользователя.

  Требуется:
  - библиотека simplejson (http://pypi.python.org/pypi/simplejson/)
  - библиотека urllib
  - библиотека os
  - метод time.time()

  Необходимо в settings.py добавить переменные:

  TWITTER_ACCOUNT = 'Skaizer' # Логин пользователя, чью ленту будем читать.
  TWITTER_CACHE_PERIOD = 3600 # Частота обновления кеша в миллисекундах, когда 0, кеш игнорируется.
  TWITTER_CACHE_FILE = 'last_twitt.txt'; # Имя файла, куда будем дампить кеш. Строится путь относительно MEDIA_ROOT.

  """
  def __init__(self):
    self.twitt_file = getattr(settings,'MEDIA_ROOT','./') + getattr(settings,'TWITTER_CACHE_FILE','last_twitt.txt')
    self.cache_period = getattr(settings,'TWITTER_CACHE_PERIOD',0)
    self.username = getattr(settings,'TWITTER_ACCOUNT',None)

  def returnLastTwitt(self):
    """
    Возвращаем кеш, если он имеется, и по времени существует не менее установленного в конфигах периода.
    Иначе возвращаем сообщение из инета.
    """
    if self.cache_period !=0:
      if os.path.exists(self.twitt_file):
        if self.cache_period > self.getCacheDateDiff():
          return self.getLastFromCache()
    return self.getLastFromWeb()

  def getLastFromWeb(self):
    """
    Скачивает твиттер ленту в Интернет и выбирает последнее сообщение
    """
    feed = 'http://twitter.com/statuses/user_timeline/' + self.username + '.json';
    feed_obj  = simplejson.loads(urllib.urlopen(feed).read())
    last_twitt = feed_obj[0]['text']
    self.cache_twitt(last_twitt.encode("utf_8"))
    return last_twitt

  def cache_twitt(self, msg):
    """
    Записывает последний твитт в локальный файл
    """
    handle = open(self.twitt_file,'w')
    handle.write(msg)
    handle.close()

  def getCacheDateDiff(self):
    """
    Считает, как давно был создан файл кеша в миллисекундах.
    """
    diff = float(time()) - float(os.path.getmtime(self.twitt_file))
    return diff

  def getLastFromCache(self):
    """
    Возвращает закешированное сообщение из файла.
    """
    handle = open(self.twitt_file,'r')
    cached_twitt = handle.read()
    handle.close()
    return cached_twitt

  def getLast(self):
    if not self.username:
      return False
    return self.returnLastTwitt()</code></pre>
<p>Вот так класс используется:</p>
<pre><code class="python">def last_twitt():
    t = Twitter()
    return {
      'last_twitt' : t.getLast(),
    }</code></pre>
<p>Все просто, без заморочек, пользуйтесь на здоровье.</p>
<p><b>PS</b>: Кстати у нас новый осенний логотип, очень симпатично получилось на наш взгляд.</p>
<p><b>Интересно почитать:</b></p>
<ul>
<li><a href="http://rotorweb.ru/mysli-vslux/kak-dobavit-raznye-ikonki-k-punktam-menyu-ispolzuya-tolko-css.html">Как добавить разные иконки к пунктам меню, используя только CSS</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.codeisart.ru/python-the-latest-messages-from-twitter/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Сравнение значений в шаблонах Django</title>
		<link>http://www.codeisart.ru/comparison-of-values-in-django-templates/</link>
		<comments>http://www.codeisart.ru/comparison-of-values-in-django-templates/#comments</comments>
		<pubDate>Fri, 15 May 2009 09:57:49 +0000</pubDate>
		<dc:creator>Skaizer</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.codeisart.ru/?p=1493</guid>
		<description><![CDATA[Недавно столкнулся с&#160;проблемой, связанной с&#160;ограниченностью шаблонного языка Django, а&#160;именно невозможностью сравнить два значения между собой (больше, меньше и&#160;т.д.). Варианты вроде: {% if some_val > 4 %} &#60;p&#62;Истина&#60;/p&#62; {% else %} &#60;p&#62;Ложь&#60;/p&#62; {% endif %} вызывают ошибку. В&#160;поисках решения я&#160;нагуглил библиотеку, разработанную Джеймсом Беннеттом (James Bennett)&#160;&#8212;&#160;django-template-utils. Для использования достаточно добавить библиотеку в&#160;Python окружение, можно посредством установки: [...]]]></description>
			<content:encoded><![CDATA[<p>Недавно столкнулся с&nbsp;проблемой, связанной с&nbsp;ограниченностью шаблонного языка Django, а&nbsp;именно невозможностью сравнить два значения между собой (больше, меньше и&nbsp;т.д.). Варианты вроде:</p>
<pre><code class="html">{% if some_val > 4 %}
&lt;p&gt;Истина&lt;/p&gt;
{% else %}
&lt;p&gt;Ложь&lt;/p&gt;
{% endif %}</code></pre>
<p>вызывают ошибку.</p>
<p>В&nbsp;поисках решения я&nbsp;нагуглил библиотеку, разработанную Джеймсом Беннеттом (James Bennett)&nbsp;&mdash;&nbsp;<a href="http://django-template-utils.googlecode.com/">django-template-utils</a>.</p>
<p><span id="more-1493"></span></p>
<p>Для использования достаточно добавить библиотеку в&nbsp;Python окружение, можно посредством установки:</p>
<pre><code class="bash">tar zxvf template_utils-0.4p2.tar.gz
cd&nbsp;template_utils-0.4p2
python setup.py install
</code></pre>
<p>А&nbsp;можно просто добавить приложение в&nbsp;<code>INSTALLED_APPS</code> проекта.</p>
<p>В&nbsp;результате вам будут доступны функции сравнения значений, и&nbsp;другие полезные возможности в&nbsp;шаблонах Django.</p>
<p>Например проверка значения на&nbsp;условие &laquo;<i>меньше или равно 4х</i>&raquo;:</p>
<pre><code class="html">{% load comparison %}
{% if_less_or_equal some_val 4 %}
&lt;p&gt;Истина&lt;/p&gt;
{% else %}
&lt;p&gt;Ложь&lt;/p&gt;
{% endif_less_or_equal %}</code></pre>
<p>Ссылка на&nbsp;библиотеку: <a href="http://django-template-utils.googlecode.com/">django-template-utils.googlecode.com</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.codeisart.ru/comparison-of-values-in-django-templates/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Python, Django: Автоматический ресайз загружаемых изображений</title>
		<link>http://www.codeisart.ru/python-django-automatically-resize-uploaded-images/</link>
		<comments>http://www.codeisart.ru/python-django-automatically-resize-uploaded-images/#comments</comments>
		<pubDate>Tue, 28 Apr 2009 21:46:57 +0000</pubDate>
		<dc:creator>Skaizer</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.codeisart.ru/?p=1476</guid>
		<description><![CDATA[На&#160;данный момент мы&#160;занимаемся разработкой городской социальной сети. Не&#160;&#171;убийцы&#187; Вконтакте и&#160;Одноклассников, ее&#160;задачи будут в&#160;корне иными. В&#160;качестве программной основы был выбран Django, написанный на&#160;языке Python. Разрабатывая интерфейс управления личной информацией необходимо было решить задачу: при загрузке пользователем собственной фотографии, ее&#160;размер должен быть не&#160;более некоторых значений, определенных настройками системы, если фото больше, то&#160;необходимо произвести ресайз с&#160;сохранением пропорций. В&#160;этой [...]]]></description>
			<content:encoded><![CDATA[<p>На&nbsp;данный момент мы&nbsp;занимаемся разработкой городской социальной сети. Не&nbsp;&laquo;убийцы&raquo; Вконтакте и&nbsp;Одноклассников, ее&nbsp;задачи будут в&nbsp;корне иными. В&nbsp;качестве программной основы был выбран Django, написанный на&nbsp;языке Python.</p>
<p>Разрабатывая интерфейс управления личной информацией необходимо было решить задачу: при загрузке пользователем собственной фотографии, ее&nbsp;размер должен быть не&nbsp;более некоторых значений, определенных настройками системы, если фото больше, то&nbsp;необходимо произвести ресайз с&nbsp;сохранением пропорций.</p>
<p>В&nbsp;этой статья я&nbsp;представлю наше решение поставленной задачи. Возможно, оно окажется не&nbsp;оптимальным, поэтому с&nbsp;удовольствием выслушаю ваши комментарии!</p>
<p><span id="more-1476"></span></p>
<h3>Django, ресайз изображений</h3>
<p>Функция ресайза изображения. Автоматически изменяет размер, сохраняя пропорции:</p>
<pre><code class="python">def imageResize(data, output_size):
  &quot;&quot;&quot;
  Resize image for thumbnails and preview
  data&nbsp;&mdash;&nbsp;image for resize
  output_size&nbsp;&mdash;&nbsp;turple, contains width and height of&nbsp;output image, for example (200, 500)
  &quot;&quot;&quot;

  from PIL import Image

  image = Image.open(data)
  m_width = float(output_size[0])
  m_height = float(output_size[1])
  if&nbsp;image.mode not in&nbsp;('L', 'RGB'):
    image = image.convert('RGB')
  w_k = image.size[0]/m_width
  h_k = image.size[1]/m_height
  if&nbsp;output_size < image.size:
    if w_k > h_k:
      new_size = (m_width, image.size[1]/w_k)
    else:
      new_size = (image.size[0]/h_k, m_height)
  else:
    new_size = image.size
  return image.resize(new_size,Image.ANTIALIAS)</code></pre>
<p>Например, при максимально возможных размерах изображения <i>200&times;500&nbsp;px</i>,&nbsp;загружая изображения на&nbsp;<i>1024&times;768&nbsp;px</i>,&nbsp;новые изображения будут&nbsp;&mdash;&nbsp;<i>200&times;150&nbsp;px</i>.</p>
<p>Далее перекрываем стандартный джанговский <code class="python">models.ImageField</code>, расширяя метод <code class="python">save_form_data</code>, в&nbsp;котором и&nbsp;будем производить ресайз:</p>
<pre><code class="python">class AvatarImageField(models.ImageField):
  def save_form_data(self, instance, data):
    from StringIO import StringIO
    from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile

    if&nbsp;data and isinstance(data, UploadedFile):
      image = imageResize(data, settings.AVATAR_SIZE)
      new_image = StringIO()
      image.save(new_image, 'JPEG', quality=85)
      data = SimpleUploadedFile(data.name, new_image.getvalue(), data.content_type)

      # Удаление старого файла
      previous = u'%s%s' % (settings.MEDIA_ROOT, instance.avatar)
      if&nbsp;os.path.isfile(previous):
        os.remove(previous)
      # -
    super(AvatarImageField, self).save_form_data(instance, data)</code></pre>
<p>Определяем функцию, которая будет возвращать путь к&nbsp;директории сохранения файла, а&nbsp;так&nbsp;же имя файла. Она будет возвращать нужный путь для <code class="python">upload_to</code> файлового поля.  В&nbsp;нашем случае имя файла генерируется некоторой хэш-функцией:</p>
<pre><code class="python">  def make_upload_path(instance, filename, prefix = False):
    # Переопределение имени загружаемого файла.
    from utils.hashfunc import get_hash

    filename = 'a_' + get_hash('md5') + '.jpg'
    return u&quot;%s/%s&quot; % (settings.AVATAR_UPLOAD_DIR, filename)</code></pre>
<p>И,&nbsp;собственно, сама модель данных, поле <code class="python">avatar</code> определяется нашим перекрытым классом <code class="python">AvatarImageField</code>, следовательно, наследует всю его функциональность:</p>
<pre><code class="python">class Avatars(models.Model):
  user = models.ForeignKey(User, unique=True)
  avatar = AvatarImageField(
    upload_to=make_upload_path,
    null = False,
    blank = False,
    )</code></pre>
<p>Готово. Теперь все загружаемые изображение, превышающие определенные в&nbsp;системе размеры будут ресайзиться, сохраняя пропорции.</p>
<p><b>С удовольствием готов ответить на вопросы, а так же выслушать конструктивную критику этого метода!</b></p>
<h3>Говорим спасибо</h3>
<p>Выражаю благодарность <a href="http://softwaremaniacs.org/about/">Ивану Сагалаеву</a> за&nbsp;его ресурс о&nbsp;программировании в&nbsp;целом и&nbsp;в&nbsp;частности на&nbsp;Python. На&nbsp;этапе ознакомления с&nbsp;Django почти с&nbsp;не&nbsp;покидали его страницы.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.codeisart.ru/python-django-automatically-resize-uploaded-images/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Python: Алгоритм Шинглов — поиск нечетких дубликатов текста</title>
		<link>http://www.codeisart.ru/python-shingles-algorithm/</link>
		<comments>http://www.codeisart.ru/python-shingles-algorithm/#comments</comments>
		<pubDate>Mon, 19 Jan 2009 10:54:22 +0000</pubDate>
		<dc:creator>Skaizer</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[SEO/SEM]]></category>
		<category><![CDATA[Алгоритмы]]></category>

		<guid isPermaLink="false">http://www.codeisart.ru/?p=1106</guid>
		<description><![CDATA[В&#160;этой статье я&#160;расскажу об&#160;алгоритме поиска нечетких дубликатов под названием &#171;Алгоритм Шинглов&#187;. А&#160;так&#160;же реализую данный алгоритм на&#160;языке Python. Почему я&#160;решил изучить данный алгоритм? Сам я&#160;являюсь SEO-шником, занимаюсь продвижением сайтов и&#160;так далее&#8230; Соответственно, моя работа заключается в&#160;изменении выдачи поисковой системы по&#160;определенному запросу. Но&#160;проработав более года в&#160;этом направлении меня заинтересовала внутренняя часть поисковых систем. Как они борются с&#160;поисковым [...]]]></description>
			<content:encoded><![CDATA[<p>В&nbsp;этой статье я&nbsp;расскажу об&nbsp;алгоритме поиска нечетких дубликатов под названием &laquo;<strong class="normal">Алгоритм Шинглов</strong>&raquo;. А&nbsp;так&nbsp;же реализую данный алгоритм на&nbsp;языке Python.</p>
<p>Почему я&nbsp;решил изучить данный алгоритм? Сам я&nbsp;являюсь SEO-шником, занимаюсь продвижением сайтов и&nbsp;так далее&#8230; Соответственно, моя работа заключается в&nbsp;изменении выдачи поисковой системы по&nbsp;определенному запросу. Но&nbsp;проработав более года в&nbsp;этом направлении меня заинтересовала внутренняя часть поисковых систем. Как они борются с&nbsp;поисковым спамом, как ранжируют документы и&nbsp;т.д. Поиск нечетких дубликатов позволяет поисковой системе исключить из&nbsp;выдачи клоны или частично похожие страницы (<i>под словом частично я&nbsp;подразумеваю некоторое значение, при котором в&nbsp;конкретной поисковой системе два документа будут определяться как почти одинаковыми</i>).</p>
<p><span id="more-1106"></span></p>
<p>Одним из&nbsp;таких алгоритмов является &laquo;<strong class="normal">Алгоритм Шинглов</strong>&raquo; (шингл на&nbsp;английском означает чешуйка).</p>
<h3>Немного теории</h3>
<p>Поиск нечетких дубликатов позволяет предположить, являются&nbsp;ли два объекта частично одинаковыми или нет. Под объектом могут пониматься текстовые файлы и&nbsp;другие типы данных. Мы&nbsp;будем работать с&nbsp;текстом, но&nbsp;поняв, как работает алгоритм, вам не&nbsp;составит труда перенести мою реализацию на&nbsp;необходимые вам объекты.</p>
<p>Обратите внимание, задачей не&nbsp;стоит определить абсолютное значение схожести объектов, а&nbsp;так&nbsp;же выделения в&nbsp;каждом из&nbsp;объектов схожих частей. Нам необходимо только предположить, являются&nbsp;ли объекты почти дубликатами или нет.</p>
<h3>Где может применяться данный алгоритм?</h3>
<p>Как я&nbsp;уже писал выше, он&nbsp;может быть применен в&nbsp;поисковой системе для очистки поисковой выдачи. Так&nbsp;же данный алгоритм может использоваться для кластеризации документов по&nbsp;их&nbsp;схожести.</p>
<h3>Почти одинаковый текст</h3>
<p>Рассмотрим задачу алгоритма на&nbsp;примере текста. Допустим, мы&nbsp;имеем файл с&nbsp;текстом в&nbsp;8&nbsp;абзацев. Делаем его полную копию, а&nbsp;затем переписываем только последний абзац. 7&nbsp;из&nbsp;8&nbsp;абзацев второго файла являются полной копией оригинала.</p>
<p>Другой пример, посложнее. Если мы&nbsp;в&nbsp;копии оригинального текста перепишем каждое 5-6е предложение, то&nbsp;текст по-прежнему будет являться почти дублем.</p>
<p>Представим, что мы&nbsp;имеем большой форум или портал, где контент составляется пользователями. Как показали наблюдения, пользователи имеют привычку заниматься копи-пастом, и&nbsp;кражей контента, лишь немного изменив его. На&nbsp;нашем сайте имеется поисковый механизм. Пользователь вводит какой-либо запрос, и&nbsp;на&nbsp;первых позициях получает десяток текстов, которые отличаются только одним или двумя предложениями. Естественно ему не&nbsp;интересно просматривать их&nbsp;все, и&nbsp;он&nbsp;понимает, что поисковая система работает некорректно.</p>
<p>Поступим логичнее. В&nbsp;поисковой выдаче вместо десятка практически одинаковых документов покажем один из&nbsp;них, например, наиболее релевантный запросу, а&nbsp;ниже дадим ссылочку на&nbsp;список так&nbsp;же найденных страниц, похожих на&nbsp;него.</p>
<p>И&nbsp;таким образом, при наличии множества почти одинакового текста мы&nbsp;распределим его на&nbsp;группы и&nbsp;предоставим пользователю ссылку на&nbsp;эти группы, вместо того, чтобы сваливать все в&nbsp;кучу.</p>
<h3>Как работает алгоритм Шинглов?</h3>
<p>Итак, мы&nbsp;имеем 2&nbsp;текста и&nbsp;нам нужно предположить, являются&nbsp;ли они почти дубликатами. Реализация алгоритма подразумевает несколько этапов:</p>
<ul>
<li>канонизация текстов;</li>
<li>разбиение текста на&nbsp;шинглы;</li>
<li>нахождение контрольных сумм;</li>
<li>поиск одинаковых подпоследовательностей.</li>
</ul>
<p>Теперь поконкретнее. В&nbsp;алгоритме шинглов реализовано сравнение контрольных сумм текстов. В&nbsp;своей реализации я&nbsp;использую <a href="http://ru.wikipedia.org/wiki/%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B8%D0%B7%D0%B1%D1%8B%D1%82%D0%BE%D1%87%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4">CRC32</a>, но&nbsp;применимы и&nbsp;другие, например <a href="http://ru.wikipedia.org/wiki/SHA1">SHA1</a>&nbsp;или <a href="http://ru.wikipedia.org/wiki/MD5">MD5</a> и&nbsp;т.д. Как известно, контрольные суммы статических функций очень чувствительны к&nbsp;изменениям. Например, контрольные суммы двух следующих текстов будут в&nbsp;корне отличаться:</p>
<ul>
<li>Текст: &laquo;My&nbsp;war is&nbsp;over.&raquo; Контрольная сумма: 1759088479</li>
<li>Текст: &laquo;My&nbsp;war is&nbsp;over!&raquo; Контрольная сумма: -127495474</li>
</ul>
<p>Как сказал <a href="http://www.codeisart.ru/wp-content/uploads/2009/01/john-rambo.jpg">Джон Рэмбо</a>: &laquo;My&nbsp;war is&nbsp;over&raquo;. Но&nbsp;сказать он&nbsp;это мог по-разному, громко восклицая или тихо шепча, так и&nbsp;у&nbsp;нас, отличие второй фразы от&nbsp;первой заключается всего лишь в&nbsp;восклицательном знаке в&nbsp;конце фразы, но&nbsp;как видим, контрольные суммы абсолютно разные.</p>
<p>При поиске почти дубликата нас не&nbsp;интересуют всякие там восклицательные знаки, точки, запятые и&nbsp;т.д. Нас интересуют только слова (не&nbsp;союзы, предлоги и&nbsp;т.д., а&nbsp;именно слова).</p>
<p>Следовательно, ставится задача: очистить текст от&nbsp;ненужных нам знаков и&nbsp;слов, которые не&nbsp;несут смысла при сравнении, это и&nbsp;называется &laquo;привести текст к&nbsp;канонической форме&raquo;.</p>
<p>Итак, нам необходимо создать набор символов, которые нужно исключить из&nbsp;обоих текстов, т.е. приведения его к&nbsp;форме, пригодной для нашего сравнения.</p>
<p>Давайте что-нибудь напрограммируем. Допустим, мы&nbsp;имеем два текста, занесем их&nbsp;в&nbsp;2&nbsp;переменных: source1 и&nbsp;source2. Нужно их&nbsp;почистить от&nbsp;ненужных символов и&nbsp;слов. Определим наборы стоп-слов и&nbsp;стоп-символов.</p>
<pre><code class="python">stop_symbols = '.,!?:;-\n\r()'

stop_words = (u'это', u'как', u'так',
  u'и', u'в', u'над',
  u'к', u'до', u'не',
  u'на', u'но', u'за',
  u'то', u'с', u'ли',
  u'а', u'во', u'от',
  u'со', u'для', u'о',
  u'же', u'ну', u'вы',
  u'бы', u'что', u'кто',
  u'он', u'она')</code></pre>
<p>В&nbsp;стоп-символы мы&nbsp;включили знаки препинаний, знаки переноса строки и&nbsp;табуляции. В&nbsp;стоп-лова мы&nbsp;включили союзы, предлоги и&nbsp;прочие слова, которые при сравнении не&nbsp;должны влиять на&nbsp;результат.</p>
<p>Создадим функцию, которая будет производить канонизацию текста:</p>
<pre><code class="python">def canonize(source):
  stop_symbols = '.,!?:;-\n\r()'

  stop_words = (u'это', u'как', u'так',
    u'и', u'в', u'над',
    u'к', u'до', u'не',
    u'на', u'но', u'за',
    u'то', u'с', u'ли',
    u'а', u'во', u'от',
    u'со', u'для', u'о',
    u'же', u'ну', u'вы',
    u'бы', u'что', u'кто',
    u'он', u'она')

  return ( [x for x in [y.strip(stop_symbols) for y in source.lower().split()] if x and (x not in stop_words)] )</code></pre>
<p>Функция <code>canonize</code> очищает текст от&nbsp;стоп-символов и&nbsp;стоп-слов, приводит все символы строки к&nbsp;нижнему регистру и&nbsp;возвращает список, оставшихся после чистки слов.</p>
<p>Вообще, приведение текста к&nbsp;единой канонической форме не&nbsp;ограничено действиями, которые я&nbsp;описал. Можно, например, каждое из&nbsp;существительных приводить к&nbsp;единственному числу, именительному падежу и&nbsp;т.д., для этого нужно подключать морфологические анализаторы русского языка (или других языков, где необходимы эти действия).</p>
<p>Итак, тексты у&nbsp;нас очищены от&nbsp;всего лишнего. Теперь необходимо разбить каждый из&nbsp;них на&nbsp;подпоследовательности&nbsp;&mdash;&nbsp;шинглы. На&nbsp;практике я&nbsp;применяю подпоследовательности длинной в&nbsp;10&nbsp;слов. Из&nbsp;выделенных нами шинглов далее будут находиться контрольные суммы.</p>
<p>Разбиение на&nbsp;шинглы будет происходить внахлест через одно слово, а&nbsp;не&nbsp;встык, т.е., имеем текст:</p>
<blockquote><p>&laquo;Разум дан человеку для того, чтобы он&nbsp;разумно жил, а&nbsp;не&nbsp;для того только, чтобы он&nbsp;понимал, что он&nbsp;неразумно живет.&raquo;&copy; В.&nbsp;Г.&nbsp;Белинский</p>
</blockquote>
<p>После обработки нашей функцией текст примет следующий вид:</p>
<blockquote><p>разум дан человеку того чтобы разумно жил того только чтобы понимал неразумно живет</p>
</blockquote>
<p>Это и&nbsp;есть каноническая форма. Теперь нужно разбить ее&nbsp;на&nbsp;шинглы длиной в&nbsp;10&nbsp;слов. Так как шинглы составляются внахлест, то&nbsp;всего шинглов <code class="python">len(source)-(shingleLen-1)</code>, т.е. количество слов в&nbsp;тексте минус длина шинглов плюс 1.</p>
<p>Шинглы будут выглядеть следующим образом:</p>
<ul>
<li>Sh1&nbsp;= разум дан человеку того чтобы разумно жил того только чтобы</li>
<li>Sh2&nbsp;= дан человеку того чтобы разумно жил того только чтобы понимал</li>
<li>Sh3&nbsp;= человеку того чтобы разумно жил того только чтобы понимал неразумно</li>
<li>Sh4&nbsp;= того чтобы разумно жил того только чтобы понимал неразумно живет</li>
</ul>
<p>Напишем функцию, которая производит разбиение текста на&nbsp;шинглы:</p>
<pre><code class="python">def genshingle(source):
  shingleLen = 10 #длина шингла
  out = []
  for i in range(len(source)-(shingleLen-1)):
    out.append (' '.join( [x for x in source[i:i+shingleLen]] ).encode('utf-8'))

  return out</code></pre>
<p>Но&nbsp;так как нас интересую именно контрольные суммы шинглов, то&nbsp;соответственно изменим нашу функцию. Мы&nbsp;будем использовать алгоритм хэширования CRC32, подключим библиотеку binascii, которая содержит в&nbsp;себе нужный нам метод. Измененная функция:</p>
<pre><code class="python">def genshingle(source):
import binascii
shingleLen = 10 #длина шингла
out = []
for i in range(len(source)-(shingleLen-1)):
out.append (binascii.crc32(' '.join( [x for x in source[i:i+shingleLen]] ).encode('utf-8')))

return out</code></pre>
<p>Наш пример выдаст следующие наборы контрольных сумм:</p>
<pre><code class="python">[1313803605, -1077944445, -2009290115, 1772759749]</code></pre>
<p>Таким образом, через наши 2&nbsp;функции нужно прогнать 2&nbsp;сравниваемых текста и&nbsp;мы&nbsp;получим 2&nbsp;множества контрольных сумм шинглов, теперь необходимо найти их&nbsp;пересечения.</p>
<pre><code class="python">def compaire (source1,source2):
  same = 0
  for i in range(len(source1)):
    if source1[i] in source2:
    same = same + 1

  return same*2/float(len(source1) + len(source2))*100</code></pre>
<p>Вот и&nbsp;все, функция нам вернет процент схожести двух текстов.</p>
<h3>Скомпонованный код</h3>
<pre><code class="python"># -*- coding: UTF-8 -*-
if __name__ == '__build__':
    raise Exception

def canonize(source):
        stop_symbols = '.,!?:;-\n\r()'

        stop_words = (u'это', u'как', u'так',
        u'и', u'в', u'над',
        u'к', u'до', u'не',
        u'на', u'но', u'за',
        u'то', u'с', u'ли',
        u'а', u'во', u'от',
        u'со', u'для', u'о',
        u'же', u'ну', u'вы',
        u'бы', u'что', u'кто',
        u'он', u'она')

        return ( [x for x in [y.strip(stop_symbols) for y in source.lower().split()] if x and (x not in stop_words)] )

def genshingle(source):
    import binascii
    shingleLen = 10 #длина шингла
    out = []
    for i in range(len(source)-(shingleLen-1)):
        out.append (binascii.crc32(' '.join( [x for x in source[i:i+shingleLen]] ).encode('utf-8')))

    return out

def compaire (source1,source2):
    same = 0
    for i in range(len(source1)):
        if source1[i] in source2:
            same = same + 1

    return same*2/float(len(source1) + len(source2))*100

def main():
    text1 = u'' # Текст 1 для сравнения
    text2 = u'' # Текст 2 для сравнения

    cmp1 = genshingle(canonize(text1))
    cmp2 = genshingle(canonize(text2))

    print compaire(cmp1,cmp2)

# Start program
main()</code></pre>
<p>Я&nbsp;показал именно реализацию ядра алгоритма, но&nbsp;его можно дорабатывать до&nbsp;бесконечности, увеличивая его производительность и&nbsp;точность сравнения.</p>
<p>Сам по&nbsp;себе алгоритм достаточно ресурсоемкий, поэтому не&nbsp;помешает для него создать механизм кеширования, который будет особенно актуален для сравнения наборов файлов, чтобы не&nbsp;высчитывать контрольные суммы каждый раз.</p>
<p>Так&nbsp;же, для увеличения производительности при обработке больших объемов текста можно сравнивать не&nbsp;все полученные контрольные суммы, а&nbsp;только те,&nbsp;которые, например, делятся на&nbsp;25,&nbsp;или любое целое число в&nbsp;пределах от&nbsp;10&nbsp;до&nbsp;40. Как показали тесты, это дает <b>значительный(!)</b> прирост скорости и&nbsp;не&nbsp;сильно уменьшает точность, но&nbsp;только при обработке больших объемов.</p>
<p>Существуют модифицированные версии &laquo;<strong class="normal">Алгоритма шинглов</strong>&raquo;&nbsp;&mdash;&nbsp;&laquo;Алгоритм супершинглов&raquo; и&nbsp;&laquo;Алгоритма мегашинглов&raquo;, их&nbsp;реализацию я&nbsp;представлю позже.</p>
<p><b>Скачать </b> <a class="attach zip" href="http://www.codeisart.ru/files/shingles-python.zip" title="Ссылка на скачивание алгоритма шинглов на языке Python">алгоритм шинглов на Python</a>.</p>
<p><b>Материалы по теме:</b></p>
<ul>
<li><a href="http://rcdl2007.pereslavl.ru/papers/paper_65_v1.pdf">Сравнительный анализ методов определения нечетких дубликатов для Web-документов</a> Зеленков Ю.Г, Сегалович И.В. 2007</li>
<li><a href="http://company.yandex.ru/articles/article10.xml">Как работают поисковые системы</a> Сегалович И.В.</li>
</ul>
<blockquote><p><b>Спонсор поста</b>: Агентство &laquo;Web++&raquo;&nbsp;&mdash;&nbsp;продвижение и <a href="http://www.webpp.ru/" title="Создание и продвижение сайтов в Волгограде">создание сайтов в Волгограде</a>.</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.codeisart.ru/python-shingles-algorithm/feed/</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Served from: www.codeisart.ru @ 2012-02-04 09:36:17 -->
