Slugifikasi string dengan Python

97

Saya sedang mencari cara terbaik untuk "slugify" string apa itu "slug" , dan solusi saya saat ini didasarkan pada resep ini

Saya telah mengubahnya sedikit menjadi:

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)

Adakah yang melihat ada masalah dengan kode ini? Ini berfungsi dengan baik, tetapi mungkin saya melewatkan sesuatu atau Anda tahu cara yang lebih baik?

Zygimantas
sumber
apakah Anda bekerja dengan banyak unicode? jika demikian, re.sub terakhir mungkin lebih baik jika Anda membungkus unicode () di sekitarnya, Inilah yang dilakukan django. Selain itu, [^ a-z0-9] + dapat disingkat untuk menggunakan \ w. lihat django.template.defaultfilters, ini dekat dengan milik Anda, tapi sedikit lebih halus.
Mike Ramirez
Apakah karakter unicode diperbolehkan di URL? Juga, saya telah mengubah \ w menjadi a-z0-9 karena \ w menyertakan _ karakter dan huruf besar. Huruf diatur ke huruf kecil terlebih dahulu, jadi tidak akan ada huruf besar yang cocok.
Zygimantas
'_' valid (tapi pilihan Anda, Anda memang bertanya), unicode adalah persen karakter yang dienkode.
Mike Ramirez
Terima kasih Mike. Nah, saya mengajukan pertanyaan yang salah. Apakah ada alasan untuk menyandikannya kembali ke string unicode, jika kita sudah mengganti semua karakter kecuali "az", "0-9" dan "-"?
Zygimantas
Untuk django, saya yakin penting bagi mereka untuk memiliki semua string sebagai objek unicode untuk kompatibilitas. Itu pilihanmu jika kamu menginginkan ini.
Mike Ramirez

Jawaban:

146

Ada paket python bernama python-slugify, yang melakukan pekerjaan slugifikasi dengan cukup baik:

pip install python-slugify

Bekerja seperti ini:

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

Lihat Contoh lainnya

Paket ini melakukan lebih dari apa yang Anda posting (lihat sumbernya, ini hanya satu file). Proyek ini masih aktif (diperbarui 2 hari sebelum saya awalnya menjawab, lebih dari tujuh tahun kemudian (terakhir diperiksa 2020-06-30), masih diperbarui).

hati-hati : Ada paket kedua, bernama slugify. Jika Anda memiliki keduanya, Anda mungkin mendapatkan masalah, karena keduanya memiliki nama yang sama untuk impor. Yang baru saja disebutkan slugifytidak melakukan semua yang saya periksa dengan cepat: "Ich heiße"menjadi "ich-heie"(seharusnya "ich-heisse"), jadi pastikan untuk memilih yang benar, saat menggunakan pipatau easy_install.

kratenko
sumber
6
python-slugifydilisensikan di bawah MIT, tetapi menggunakan Unidecodeyang dilisensikan di bawah GPL, jadi mungkin tidak cocok untuk beberapa proyek.
Rotareti
@Rotareti Bisakah Anda jelaskan kepada saya mengapa tidak dapat memenuhi semua proyek? Tidak bisakah kita menggunakan apapun di bawah lisensi MIT atau GPL dan memasukkannya ke dalam perangkat lunak komersial? Saya pikir satu-satunya batasan adalah menempatkan lisensi selain kode yang kami kembangkan. Apakah aku salah?
Ghassem Tofighi
1
@GhassemTofighi Singkatnya: Anda dapat menggunakannya dalam perangkat lunak komersial Anda, tetapi jika Anda menggunakannya, Anda harus membuka kode sumber juga. Bagaimanapun, IANAL dan ini bukan nasihat hukum.
Rotareti
@GhassemTofighi mungkin lihat softwareengineering.stackexchange.com/q/47032/71504 tentang topik itu
kratenko
1
@Rotareti python-slugifysekarang secara default menggunakan Lisensi Artistik text-unidecodealih-alih lisensi GPL Unidecode, menangani masalah lisensi Anda. github.com/un33k/python-slugify/commit/…
Emilien
31

Instal formulir unidecode dari sini untuk dukungan unicode

pip instal 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

pengguna1078810
sumber
1
hai, ini agak aneh tetapi memberikan hasil seperti itu "my-custom-ndud-d-d3-4-d2d3-4nd-d-"
derevo
1
@derevo yang terjadi saat Anda tidak mengirim string unicode. Ganti slugify("My custom хелло ворлд")dengan slugify(u"My custom хелло ворлд"), dan itu akan berhasil.
kratenko
9
Saya menyarankan agar tidak menggunakan nama variabel seperti str. Ini menyembunyikan strtipe bawaan .
crodjer
2
unidecode adalah GPL, yang mungkin tidak cocok untuk beberapa orang.
Jorge Leitao
Bagaimana dengan reslugifying atau deslugifying.
Ryan Chou
11

Ada paket python bernama awesome-slugify :

pip install awesome-slugify

Bekerja seperti ini:

from slugify import slugify

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

halaman github keren-slugify

voronin
sumber
2
Paket yang bagus! Tapi hati-hati, ini berlisensi di bawah GPL.
Rotareti
1
Perhatian: ini tidak akan secara otomatis. Menurunkan () url Anda. Anda harus lari slugify(text).lower()jika menginginkannya.
Kalob Taulien
7

Ia bekerja dengan baik di Django , jadi saya tidak melihat mengapa itu bukan fungsi slugify tujuan umum yang baik.

Apakah Anda mengalami masalah dengan itu?

Nick Presta
sumber
Mungkin saja, untuk beberapa kasus, ini adalah dosis paranoia yang sehat :-)
nemesisfixx
Kode telah dipindahkan ke sini .
raylu
13
Untuk para pemalas:from django.utils.text import slugify
Spartacus
6

Masalahnya adalah dengan garis normalisasi ascii:

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

Ini disebut normalisasi unicode yang tidak mendekomposisi banyak karakter menjadi ascii. Misalnya, ini akan menghapus karakter non-ascii dari string berikut:

Mørdag -> mrdag
Æther -> ther

Cara yang lebih baik untuk melakukannya adalah dengan menggunakan modul unidecode yang mencoba mentransliterasi string ke ascii. Jadi jika Anda mengganti baris di atas dengan:

import unidecode
slug = unidecode.unidecode(s)

Anda mendapatkan hasil yang lebih baik untuk string di atas dan untuk banyak karakter Yunani dan Rusia juga:

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

Ini adalah fungsi slugify yang ada di django.utils.text Ini harus mencukupi kebutuhan Anda.

Animesh Sharma
sumber
3

Unidecode bagus; Namun, hati-hati: unidecode adalah GPL. Jika lisensi ini tidak cocok, gunakan yang ini

Mikhail Korobov
sumber
2

Beberapa opsi di GitHub:

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

Masing-masing mendukung parameter yang sedikit berbeda untuk API-nya, jadi Anda harus memeriksanya untuk mencari tahu apa yang Anda sukai.

Secara khusus, perhatikan berbagai opsi yang mereka sediakan untuk menangani karakter non-ASCII. Pydanny menulis entri blog yang sangat membantu yang menggambarkan beberapa perbedaan penanganan unicode di pustaka slugify berikut: http://www.pydanny.com/awesome-slugify-human-readable-url-slugs-from-any-string.html Entri blog ini agak ketinggalan jaman karena Mozilla unicode-slugifybukan lagi khusus-Django.

Perhatikan juga bahwa saat awesome-slugifyini GPLv3, meskipun ada masalah terbuka di mana penulis mengatakan mereka lebih suka merilis sebagai MIT / BSD, hanya saja tidak yakin dengan legalitasnya: https://github.com/dimka665/awesome-slugify/issues/ 24

Jeff Widman
sumber
1

Anda dapat mempertimbangkan untuk mengubah baris terakhir menjadi

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

karena polanya [-]+tidak berbeda dengan-+ , dan Anda tidak terlalu peduli tentang mencocokkan hanya satu tanda hubung, hanya dua atau lebih.

Tapi, tentu saja, ini kecil.

unutbu
sumber
0

Pilihan lainnya adalah boltons.strutils.slugify. Boltons memiliki beberapa fungsi berguna lainnya juga, dan didistribusikan di bawah BSDlisensi.

ostrokach.dll
sumber