Слугіфікація рядків у 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)

Хтось бачить проблеми з цим кодом? Це працює нормально, але, можливо, мені чогось не вистачає, або ви знаєте кращий спосіб?


Ви багато працюєте з Unicode? якщо так, останній файл re.sub може бути кращим, якщо ви обернете його Unicode (), це те, що робить django. Також [^ a-z0-9] + можна скоротити, використовуючи \ w. див. django.template.defaultfilters, він близький до вашого, але трохи вишуканіший.
Майк Рамірес

Чи дозволено символи Unicode в URL-адресі? Крім того, я змінив \ w на a-z0-9, оскільки \ w включає _ символ та великі літери. Букви заздалегідь встановлюються з малої літери, тому не буде відповідних великих літер.
Zygimantas

'_' є дійсним (але ваш вибір, ви вже запитували), юнікод - це відсоткові кодовані символи.
Mike Ramirez,

Дякую, Майк. Ну, я поставив неправильне запитання. Чи є яка-небудь причина кодувати його назад до рядка Unicode, якщо ми вже замінили всі символи, крім "az", "0-9" та "-"?
Zygimantas

Для django я вважаю, що для них важливо мати усі рядки як об'єкти Unicode для сумісності. Це ваш вибір, якщо ви цього хочете.
Mike Ramirez,

Відповіді:


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 дні до того, як я спочатку відповів, через сім років (востаннє перевірено 2020-06-30), він все ще оновлюється).

обережно : навколо є другий пакет, названий slugify. Якщо у вас обоє, ви можете отримати проблему, оскільки вони мають однакову назву для імпорту. Щойно названий slugifyне зробив всього, що я швидко перевірив: "Ich heiße"став "ich-heie"(повинен бути "ich-heisse"), тому обов’язково виберіть правильний при використанні pipабо easy_install.


6
python-slugifyмає ліцензію MIT, але використовує Unidecodeліцензію GPL, тому може не підходити для деяких проектів.
Ротарети

@Rotareti Не могли б ви пояснити мені, чому це не може відповідати всім проектам? Чи не можемо ми використовувати щось за ліцензією MIT або GPL та включати їх до комерційного програмного забезпечення? Я думаю, що єдиним обмеженням є надання ліцензії крім кодів, які ми розробляємо. Я помиляюся?
Ghassem Tofighi

1
@GhassemTofighi Коротше: ви можете використовувати його у своєму комерційному програмному забезпеченні, але якщо ви використовуєте його, ви також повинні відкрити свій код. У будь-якому випадку IANAL, і це не є юридичною порадою.
Ротареті

@GhassemTofighi, можливо, погляньте на softwareengineering.stackexchange.com/q/47032/71504 на цю тему
kratenko

1
python-slugifyЗараз за замовчуванням @Rotareti застосовує Художню ліцензію text-unidecodeзамість ліцензії GPL Unidecode, вирішуючи вашу проблему ліцензування. github.com/un33k/python-slugify/commit / ...
Емільену

31

Встановіть unidecode форму тут для підтримки Юнікоду

pip встановити

# -*- 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)

>>> my-custom-khello-vorld


1
привіт, це трохи дивно, але це дає для мого відгуку такий, як "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, який для деяких може не підійти.
Хорхе Лейтао

А як щодо релюгіфікації або делюгіфікації.
Ryan Chou,

11

Існує пакет python з назвою awesome-slugify :

pip install awesome-slugify

Працює так:

from slugify import slugify

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

приголомшливо-млява сторінка github


2
Гарний пакет! Але будьте обережні, це ліцензія під GPL.
Ротарети

1
Увага: це не буде автоматично .low (() ваші URL-адреси. Вам потрібно буде бігти, slugify(text).lower()якщо ви цього хочете.
Kalob Taulien

7

Це добре працює в Django , тому я не розумію, чому це не була б хорошою функцією загального призначення.

Чи виникають у вас проблеми з цим?


Цілком можливо, що в деяких випадках це здорова доза параної :-)
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. Цього має вистачити вашим вимогам.



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. Пиданні написав дуже корисну публікацію в блозі, в якій проілюстрував деякі відмінності в обробці Unicode у цих службових бібліотеках: 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)

оскільки зразок [-]+нічим не відрізняється від -+, і ви насправді не піклуєтесь про те, щоб зіставити лише один дефіс, лише два або більше.

Але, звичайно, це зовсім незначно.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.