Слагизация строк в Python

97

Я ищу лучший способ "слагать" строку, что такое "слаг" , и мое текущее решение основано на этом рецепте

Я немного изменил его на:

s = 'String to slugify'

slug = unicodedata.normalize('NFKD', s)
slug = slug.encode('ascii', 'ignore').lower()
slug = re.sub(r'[^a-z0-9]+', '-', slug).strip('-')
slug = re.sub(r'[-]+', '-', slug)

Кто-нибудь видит какие-либо проблемы с этим кодом? Он работает нормально, но, может быть, я что-то упускаю или знаете способ получше?

Зигимантас
источник
вы много работаете с юникодом? если да, то последний re.sub может быть лучше, если вы обернете его в unicode (). Это то, что делает django. Кроме того, [^ a-z0-9] + можно сократить до \ w. см. django.template.defaultfilters, он близок к вашему, но немного более точен.
Майк Рамирес,
Разрешены ли символы Юникода в URL? Кроме того, я изменил \ w на a-z0-9, потому что \ w включает символ _ и прописные буквы. Буквы заранее устанавливаются на нижний регистр, поэтому прописные буквы не совпадают.
Зигимантас
'_' действителен (но ваш выбор, вы спросили), Unicode - это символы, закодированные в процентах.
Майк Рамирес,
Спасибо, Майк. Что ж, я задал неправильный вопрос. Есть ли причина кодировать его обратно в строку Unicode, если мы уже заменили все символы, кроме «az», «0-9» и «-»?
Zygimantas
Я считаю, что для django важно иметь все строки как объекты Unicode для совместимости. Если вы этого хотите, это ваш выбор.
Майк Рамирес,

Ответы:

146

Существует пакет python с именем python-slugify, который довольно хорошо справляется с заменой:

pip install python-slugify

Работает так:

from slugify import slugify

txt = "This is a test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = "This -- is a ## test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = 'C\'est déjà l\'été.'
r = slugify(txt)
self.assertEquals(r, "cest-deja-lete")

txt = 'Nín hǎo. Wǒ shì zhōng guó rén'
r = slugify(txt)
self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren")

txt = 'Компьютер'
r = slugify(txt)
self.assertEquals(r, "kompiuter")

txt = 'jaja---lol-méméméoo--a'
r = slugify(txt)
self.assertEquals(r, "jaja-lol-mememeoo-a")

Посмотреть больше примеров

Этот пакет делает немного больше, чем то, что вы опубликовали (взгляните на исходный код, это всего лишь один файл). Проект все еще активен (обновлен за 2 дня до моего первоначального ответа, более семи лет спустя (последний раз проверено 30 июня 2020 г.), он все еще обновляется).

осторожно : есть второй пакет с именем slugify. Если у вас есть оба из них, вы можете столкнуться с проблемой, поскольку они имеют одинаковое имя для импорта. Тот, что только что назван slugify, не сделал все, что я проверил: "Ich heiße"стал "ich-heie"(должен быть "ich-heisse"), поэтому обязательно выберите правильный при использовании pipили easy_install.

Кратенко
источник
6
python-slugifyпод лицензией MIT, но использует то, Unidecodeчто под лицензией GPL, поэтому может не подходить для некоторых проектов.
Rotareti 06
@Rotareti Не могли бы вы объяснить мне, почему он не подходит для всех проектов? Разве мы не можем использовать что-либо по лицензии MIT или GPL и включать это в коммерческое программное обеспечение? Думаю, единственное ограничение - ставить лицензию помимо разрабатываемых нами кодов. Я ошибся?
Ghassem Tofighi
1
@GhassemTofighi Вкратце: вы можете использовать его в своем коммерческом программном обеспечении, но если вы его используете, вы также должны открыть исходный код своего кода. В любом случае IANAL и это не юридическая консультация.
Rotareti
@GhassemTofighi, может быть, взгляните на softwareengineering.stackexchange.com/q/47032/71504 по этой теме
Кратенко
1
@Rotareti python-slugifyтеперь по умолчанию использует Artistic License'd, text-unidecodeа не GPL-лицензию Unidecode, что позволяет решить вашу проблему лицензирования. github.com/un33k/python-slugify/commit/…
Эмильен,
31

Установите форму unidecode отсюда для поддержки Unicode

pip install unidecode

# -*- coding: utf-8 -*-
import re
import unidecode

def slugify(text):
    text = unidecode.unidecode(text).lower()
    return re.sub(r'[\W_]+', '-', text)

text = u"My custom хелло ворлд"
print slugify(text)

>>> мой-обычай-хелло-мир

user1078810
источник
1
привет, это немного странно, но это дает для моих res как это "my-custom-ndud-d-d3-4-d2d3-4nd-d-"
дерево
1
@derevo, которые случаются, когда вы не отправляете строки Unicode. Замените slugify("My custom хелло ворлд")на slugify(u"My custom хелло ворлд"), и он должен работать.
Кратенко
9
Я бы посоветовал не использовать такие имена переменных, как str. Это скрывает встроенный strтип.
Crodjer
2
unidecode - это GPL, что может не подходить для некоторых.
Хорхе Лейтао
А как насчет замены или удаления.
Райан Чоу
11

Существует пакет python с именем awesome-slugify :

pip install awesome-slugify

Работает так:

from slugify import slugify

slugify('one kožušček')  # one-kozuscek

awesome-slugify страница github

воронин
источник
2
Хорошая упаковка! Но будьте осторожны, он распространяется под лицензией GPL.
Rotareti 06
1
Внимание: это не автоматически .lower () ваши URL-адреса. Вам нужно будет бежать, slugify(text).lower()если вы этого хотите.
Калоб Таулиен
7

Он хорошо работает в Django , поэтому я не понимаю, почему это не будет хорошей функцией slugify общего назначения.

У вас с этим проблемы?

Ник Преста
источник
Возможно, что для некоторых случаев это здоровая доза паранойи :-)
nemesisfixx
Код перемещен сюда .
raylu
13
Для лентяев:from django.utils.text import slugify
Спартак
6

Проблема в строке нормализации ascii:

slug = unicodedata.normalize('NFKD', s)

Это называется нормализацией Unicode, которая не разбивает множество символов на ascii. Например, он удалит символы, отличные от ascii, из следующих строк:

Mørdag -> mrdag
Æther -> ther

Лучший способ сделать это - использовать модуль unidecode, который пытается транслитерировать строки в ascii. Итак, если вы замените указанную выше строку на:

import unidecode
slug = unidecode.unidecode(s)

Вы получите лучшие результаты для указанных выше строк, а также для многих греческих и русских символов:

Mørdag -> mordag
Æther -> aether
Бьёрн Линдквист
источник
6
def slugify(value):
    """
    Converts to lowercase, removes non-word characters (alphanumerics and
    underscores) and converts spaces to hyphens. Also strips leading and
    trailing whitespace.
    """
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    value = re.sub('[^\w\s-]', '', value).strip().lower()
    return mark_safe(re.sub('[-\s]+', '-', value))
slugify = allow_lazy(slugify, six.text_type)

Это функция slugify, присутствующая в django.utils.text. Этого должно хватить на ваши требования.

Анимеш Шарма
источник
3

Unidecode хорош; однако будьте осторожны: unidecode - это GPL. Если эта лицензия не подходит, используйте эту

Михаил Коробов
источник
2

Пара вариантов на GitHub:

  1. https://github.com/dimka665/awesome-slugify
  2. https://github.com/un33k/python-slugify
  3. https://github.com/mozilla/unicode-slugify

Каждый из них поддерживает несколько разные параметры для своего API, поэтому вам нужно просмотреть их, чтобы понять, что вы предпочитаете.

В частности, обратите внимание на различные варианты, которые они предоставляют для работы с символами, отличными от ASCII. Pydanny написал очень полезное сообщение в блоге, иллюстрирующее некоторые различия в обработке Unicode в этих slugify-библиотеках: http://www.pydanny.com/awesome-slugify-human-readable-url-slugs-from-any-string.html Это сообщение в блоге немного устарело, потому что Mozilla unicode-slugifyбольше не зависит от Django.

Также обратите внимание, что в настоящее время awesome-slugifyдействует GPLv3, хотя есть открытая проблема, когда автор говорит, что предпочел бы выпускать как MIT / BSD, просто не уверен в законности: https://github.com/dimka665/awesome-slugify/issues/ 24

Джефф Видман
источник
1

Вы можете изменить последнюю строку на

slug=re.sub(r'--+',r'-',slug)

так как шаблон [-]+ничем не отличается от -+, и вы действительно не заботитесь о сопоставлении только одного дефиса, только двух или более.

Но, конечно, это совсем мелочь.

Unutbu
источник