String Slugification in Python

91

Ich bin auf der Suche nach dem besten Weg, um einen String zu "slugifizieren", was "slug" ist , und meine aktuelle Lösung basiert auf diesem Rezept

Ich habe es ein wenig geändert zu:

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)

Hat jemand Probleme mit diesem Code? Es funktioniert gut, aber vielleicht fehlt mir etwas oder Sie kennen einen besseren Weg?

Zygimantas
quelle
Arbeiten Sie viel mit Unicode? Wenn ja, ist die letzte Wiederholung möglicherweise besser, wenn Sie Unicode () darum wickeln. Dies ist, was Django tut. Außerdem kann [^ a-z0-9] + gekürzt werden, um \ w zu verwenden. siehe django.template.defaultfilters, es liegt in Ihrer Nähe, ist aber etwas raffinierter.
Mike Ramirez
Sind Unicode-Zeichen in der URL zulässig? Außerdem habe ich \ w in a-z0-9 geändert, da \ w _ Zeichen und Großbuchstaben enthält. Buchstaben werden im Voraus auf Kleinbuchstaben gesetzt, sodass keine übereinstimmenden Großbuchstaben vorhanden sind.
Zygimantas
'_' ist gültig (aber Ihre Wahl, haben Sie gefragt), Unicode ist in Prozent codierte Zeichen.
Mike Ramirez
Danke Mike. Nun, ich habe eine falsche Frage gestellt. Gibt es einen Grund, es wieder in eine Unicode-Zeichenfolge zu codieren, wenn wir bereits alle Zeichen außer "az", "0-9" und "-" ersetzt haben?
Zygimantas
Für Django ist es meiner Meinung nach wichtig, alle Zeichenfolgen als Unicode-Objekte zu verwenden, um die Kompatibilität zu gewährleisten. Sie haben die Wahl, wenn Sie dies möchten.
Mike Ramirez

Antworten:

143

Es gibt ein Python-Paket mit dem Namen python-slugify, das ziemlich gute Arbeit beim Slugifizieren leistet:

pip install python-slugify

Funktioniert so:

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

Siehe Weitere Beispiele

Dieses Paket bietet etwas mehr als das, was Sie veröffentlicht haben (sehen Sie sich die Quelle an, es ist nur eine Datei). Das Projekt ist noch aktiv (wurde 2 Tage vor meiner ursprünglichen Antwort aktualisiert, über sieben Jahre später (zuletzt überprüft am 30.06.2020), es wird immer noch aktualisiert).

Vorsicht : Es gibt ein zweites Paket mit dem Namen slugify. Wenn Sie beide haben, tritt möglicherweise ein Problem auf, da sie für den Import denselben Namen haben. Der gerade genannte slugifyhat nicht alles getan, was ich schnell überprüft habe: "Ich heiße"wurde "ich-heie"(sollte sein "ich-heisse"), also wählen Sie unbedingt den richtigen aus, wenn Sie pipoder verwenden easy_install.

kratenko
quelle
6
python-slugifyist unter MIT lizenziert, verwendet jedoch Unidecodedie unter GPL lizenzierte Lizenz, sodass sie möglicherweise nicht für einige Projekte geeignet ist.
Rotareti
@ Rotareti Könnten Sie mir bitte erklären, warum es nicht für alle Projekte geeignet ist? Können wir nichts unter MIT- oder GPL-Lizenz verwenden und in kommerzielle Software aufnehmen? Ich denke, die einzige Einschränkung besteht darin, die Lizenz neben den von uns entwickelten Codes zu platzieren. Liege ich falsch?
Ghassem Tofighi
1
@GhassemTofighi Kurz gesagt: Sie können es in Ihrer kommerziellen Software verwenden, aber wenn Sie es verwenden, müssen Sie auch Ihren Code als Open Source verwenden. Sowieso IANAL und dies ist keine Rechtsberatung.
Rotareti
@ GhassemTofighi vielleicht einen Blick auf softwareengineering.stackexchange.com/q/47032/71504 zu diesem Thema
kratenko
1
@Rotareti verwendet python-slugifyjetzt standardmäßig die Artistic License'd text-unidecodeanstelle der GPL-Lizenz Unidecode, um Ihr Lizenzproblem zu lösen . github.com/un33k/python-slugify/commit/…
Emilien
31

Installieren Sie von hier aus das Unidecode-Formular für die Unicode-Unterstützung

Pip Unidecode installieren

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

user1078810
quelle
1
Hallo, es ist ein bisschen seltsam, aber es gibt für meine Res so "my-custom-ndud-d-d3-4-d2d3-4nd-d-"
derevo
1
@derevo, das passiert ist, wenn Sie keine Unicode-Zeichenfolgen senden. Ersetzen Sie slugify("My custom хелло ворлд")durch slugify(u"My custom хелло ворлд"), und es sollte funktionieren.
Kratenko
9
Ich würde vorschlagen, keine Variablennamen wie zu verwenden str. Dies verbirgt den eingebauten strTyp.
Crodjer
2
Unidecode ist GPL, die für einige möglicherweise nicht geeignet ist.
Jorge Leitao
Was ist mit dem Reslugifizieren oder Deslugifizieren?
Ryan Chou
11

Es gibt ein Python-Paket namens awesome-slugify :

pip install awesome-slugify

Funktioniert so:

from slugify import slugify

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

awesome-slugify Github-Seite

Voronin
quelle
2
Nettes Paket! Aber seien Sie vorsichtig, es ist unter GPL lizenziert.
Rotareti
1
Heads up: Dies wird Ihre URLs nicht automatisch .lower (). Sie müssen laufen, slugify(text).lower()wenn Sie das wollen.
Kalob Taulien
6

Es funktioniert gut in Django , daher verstehe ich nicht, warum es keine gute Allzweck-Slugify-Funktion wäre.

Hast du irgendwelche Probleme damit?

Nick Presta
quelle
Es ist möglich, dass es in einigen Fällen eine gesunde Dosis Paranoia ist :-)
nemesisfixx
Der Code wurde hierher verschoben .
Raylu
12
Für die Faulen:from django.utils.text import slugify
Spartacus
6

Das Problem ist mit der ASCII-Normalisierungslinie:

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

Es wird als Unicode-Normalisierung bezeichnet, bei der nicht viele Zeichen in ASCII zerlegt werden. Beispielsweise würden Nicht-ASCII-Zeichen aus den folgenden Zeichenfolgen entfernt:

Mørdag -> mrdag
Æther -> ther

Ein besserer Weg, dies zu tun, ist die Verwendung des Unidecode- Moduls, das versucht, Zeichenfolgen in ASCII zu transliterieren. Wenn Sie also die obige Zeile durch Folgendes ersetzen:

import unidecode
slug = unidecode.unidecode(s)

Sie erhalten bessere Ergebnisse für die oben genannten Zeichenfolgen und auch für viele griechische und russische Zeichen:

Mørdag -> mordag
Æther -> aether
Björn Lindqvist
quelle
5
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)

Dies ist die Slugify-Funktion in django.utils.text. Dies sollte Ihrer Anforderung genügen.

Animesh Sharma
quelle
3

Unidecode ist gut; Seien Sie jedoch vorsichtig: Unidecode ist GPL. Wenn diese Lizenz nicht passt, verwenden Sie diese

Mikhail Korobov
quelle
2

Einige Optionen auf GitHub:

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

Jedes unterstützt leicht unterschiedliche Parameter für seine API, daher müssen Sie durchsehen, um herauszufinden, was Sie bevorzugen.

Beachten Sie insbesondere die verschiedenen Optionen für den Umgang mit Nicht-ASCII-Zeichen. Pydanny hat einen sehr hilfreichen Blog-Beitrag geschrieben, in dem einige der Unterschiede beim Umgang mit Unicode in diesen Slugify-Bibliotheken dargestellt werden: http://www.pydanny.com/awesome-slugify-human-readable-url-slugs-from-any-string.html Dieser Blog-Beitrag ist etwas veraltet, weil Mozillaunicode-slugify nicht mehr Django-spezifisch ist.

Beachten Sie auch, dass es sich derzeit awesome-slugifyum GPLv3 handelt, obwohl es ein offenes Problem gibt, bei dem der Autor angibt, dass er es vorziehen würde, als MIT / BSD zu veröffentlichen, aber nicht sicher ist, ob dies legal ist: https://github.com/dimka665/awesome-slugify/issues/ 24

Jeff Widman
quelle
1

Sie können die letzte Zeile in ändern

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

da das Muster [-]+nicht anders ist als -+und es Ihnen nicht wirklich wichtig ist, nur einen Bindestrich, nur zwei oder mehr zu finden.

Aber das ist natürlich ziemlich gering.

unutbu
quelle
0

Eine andere Option ist boltons.strutils.slugify. Boltons hat noch einige andere nützliche Funktionen und wird unter einer BSDLizenz vertrieben.

ostrokach
quelle