Slugification string w Pythonie


97

Szukam najlepszego sposobu na "slugify" czym jest "slug" , a moje obecne rozwiązanie bazuje na tym przepisie

Zmieniłem to trochę na:

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)

Czy ktoś widzi jakieś problemy z tym kodem? Działa dobrze, ale może czegoś mi brakuje lub znasz lepszy sposób?


czy dużo pracujesz z Unicode? jeśli tak, ostatnia re.sub może być lepsza, jeśli zawiniesz wokół niego unicode (). To właśnie robi django. Ponadto znak [^ a-z0-9] + można skrócić do użycia \ w. zobacz django.template.defaultfilters, jest zbliżony do twojego, ale nieco bardziej wyrafinowany.
Mike Ramirez

Czy w adresie URL dozwolone są znaki Unicode? Zmieniłem również \ w na a-z0-9, ponieważ \ w zawiera znak _ i wielkie litery. Litery są wcześniej ustawione na małe litery, więc nie będzie pasujących wielkich liter.
Zygimantas,

'_' jest poprawne (ale twój wybór, pytałeś), unicode jest znakami zakodowanymi w procentach.
Mike Ramirez,

Dziękuję Mike. Cóż, zadałem złe pytanie. Czy jest jakiś powód, aby zakodować go z powrotem do ciągu znaków Unicode, jeśli już zastąpiliśmy wszystkie znaki oprócz „az”, „0-9” i „-”?
Zygimantas,

Uważam, że w przypadku django ważne jest, aby wszystkie ciągi znaków były obiektami Unicode w celu zapewnienia zgodności. To twój wybór, jeśli tego chcesz.
Mike Ramirez,

Odpowiedzi:


146

Istnieje pakiet o nazwie Python python-slugify, który całkiem nieźle radzi sobie ze slugowaniem:

pip install python-slugify

Działa tak:

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")

Zobacz więcej przykładów

Ten pakiet robi trochę więcej niż to, co opublikowałeś (spójrz na źródło, to tylko jeden plik). Projekt jest nadal aktywny (został zaktualizowany 2 dni przed pierwszą odpowiedzią, ponad siedem lat później (ostatnio sprawdzany 2020-06-30), nadal jest aktualizowany).

uwaga : w pobliżu jest drugi pakiet o nazwie slugify. Jeśli masz oba, możesz mieć problem, ponieważ mają tę samą nazwę do importu. Ten, który właśnie wymieniono, slugifynie zrobił wszystkiego, co szybko sprawdziłem: "Ich heiße"stał się "ich-heie"(powinien być "ich-heisse"), więc pamiętaj, aby wybrać właściwy, używając piplub easy_install.


6
python-slugifyjest objęty licencją MIT, ale korzysta z Unidecodelicencji GPL, więc może nie pasować do niektórych projektów.
Rotareti

@Rotareti Czy mógłbyś mi wyjaśnić, dlaczego nie pasuje do wszystkich projektów? Czy nie możemy używać niczego na licencji MIT lub GPL i umieszczać ich w oprogramowaniu komercyjnym? Myślę, że jedynym ograniczeniem jest umieszczenie licencji poza kodami, które tworzymy. Czy się mylę?
Ghassem Tofighi

1
@GhassemTofighi W skrócie: możesz go używać w swoim komercyjnym oprogramowaniu, ale jeśli go używasz, musisz również otworzyć kod źródłowy. W każdym razie IANAL i nie jest to porada prawna.
Rotareti

@GhassemTofighi może spojrzeć na softwareengineering.stackexchange.com/q/47032/71504 na ten temat
kratenko

1
@Rotareti python-slugifydomyślnie korzysta text-unidecodez licencji artystycznej zamiast licencji GPL Unidecode, rozwiązując Twoje problemy licencyjne. github.com/un33k/python-slugify/commit/…
Emilien

31

Zainstaluj stąd formularz unidecode , aby uzyskać obsługę Unicode

pip zainstaluj 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)

>>> my-custom-khello-vorld


1
cześć, to trochę dziwne, ale daje to dla moich rozdzielczości w ten sposób "my-custom-ndud-d-d3-4-d2d3-4nd-d-"
derevo

1
@derevo, które dzieje się, gdy nie wysyłasz ciągów znaków Unicode. Wymień slugify("My custom хелло ворлд")się slugify(u"My custom хелло ворлд"), i to powinno działać.
kratenko

9
Sugerowałbym, aby nie używać nazw zmiennych, takich jak str. To ukrywa wbudowany strtyp.
crodjer

2
unidecode to GPL, która może nie być odpowiednia dla niektórych.
Jorge Leitao,

A co z reslugifying lub deslugifying.
Ryan Chou


7

Działa dobrze w Django , więc nie rozumiem, dlaczego nie byłaby to dobra funkcja slugify ogólnego przeznaczenia.

Masz z tym jakieś problemy?


Możliwe, że w niektórych przypadkach to zdrowa dawka paranoi :-)
nemesisfixx

Kod został przeniesiony tutaj .
raylu,

13
Dla leniwców:from django.utils.text import slugify
Spartakus

6

Problem dotyczy linii normalizacji ascii:

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

Nazywa się to normalizacją Unicode, która nie rozkłada wielu znaków na ascii. Na przykład usunie znaki inne niż ASCII z następujących ciągów:

Mørdag -> mrdag
Æther -> ther

Lepszym sposobem na zrobienie tego jest użycie modułu unidecode , który próbuje transliterować ciągi znaków na ascii. Więc jeśli zamienisz powyższą linię na:

import unidecode
slug = unidecode.unidecode(s)

Uzyskasz lepsze wyniki dla powyższych ciągów, a także dla wielu znaków greckich i rosyjskich:

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)

To jest funkcja slugify obecna w django.utils.text. To powinno wystarczyć.



2

Kilka opcji na GitHub:

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

Każdy obsługuje nieco inne parametry swojego interfejsu API, więc musisz przejrzeć, aby dowiedzieć się, co wolisz.

W szczególności zwróć uwagę na różne opcje, jakie zapewniają one w przypadku znaków spoza zestawu ASCII. Pydanny napisał bardzo pomocny post na blogu, ilustrujący niektóre różnice w obsłudze Unicode w tych bibliotekach slugify: http://www.pydanny.com/awesome-slugify-human-readable-url-slugs-from-any-string.html Ten wpis na blogu jest nieco nieaktualny, ponieważ Mozilla nie unicode-slugifyjest już specyficzna dla Django.

Zwróć również uwagę, że obecnie awesome-slugifyjest to GPLv3, chociaż istnieje otwarty problem, w którym autor mówi, że wolałby wydać jako MIT / BSD, ale nie jest pewien legalności: https://github.com/dimka665/awesome-slugify/issues/ 24


1

Możesz rozważyć zmianę ostatniej linii na

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

ponieważ wzorzec [-]+nie różni się od wzorca -+i nie zależy Ci na dopasowaniu tylko jednego łącznika, tylko dwóch lub więcej.

Ale oczywiście jest to dość drobne.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.