สตริง slugification ใน Python


97

ฉันกำลังค้นหาวิธีที่ดีที่สุดในการ "slugify" สตริงว่า "slug" คืออะไรและวิธีแก้ปัญหาปัจจุบันของฉันใช้สูตรนี้

ฉันได้เปลี่ยนมันเล็กน้อยเป็น:

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 ใกล้เคียงกับของคุณ แต่ละเอียดกว่าเล็กน้อย
Mike Ramirez

อนุญาตให้ใช้อักขระ Unicode ใน URL หรือไม่ นอกจากนี้ฉันได้เปลี่ยน \ w เป็น a-z0-9 เนื่องจาก \ w มีอักขระ _ ตัวอักษรและตัวพิมพ์ใหญ่ ตัวอักษรถูกตั้งค่าเป็นตัวพิมพ์เล็กไว้ล่วงหน้าดังนั้นจะไม่มีตัวอักษรพิมพ์ใหญ่ที่จะจับคู่
Zygimantas

'_' นั้นถูกต้อง (แต่คุณได้ขอเลือกไว้) Unicode เป็นอักขระที่เข้ารหัสแบบเปอร์เซ็นต์
Mike Ramirez

ขอบคุณไมค์ครับ ฉันถามคำถามผิด มีเหตุผลใดที่จะเข้ารหัสกลับเป็นสตริง Unicode ถ้าเราเปลี่ยนอักขระทั้งหมดแล้วยกเว้น "az", "0-9" และ "-"
Zygimantas

สำหรับ django ฉันเชื่อว่าสิ่งสำคัญสำหรับพวกเขาที่จะต้องมีสตริงทั้งหมดเป็นอ็อบเจ็กต์ Unicode เพื่อความเข้ากันได้ เป็นทางเลือกของคุณหากคุณต้องการสิ่งนี้
Mike Ramirez

คำตอบ:


146

มีชื่อแพคเกจ python python-slugifyซึ่งทำงานได้ดีในการ slugifying:

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") เพื่อให้แน่ใจว่าจะเลือกหนึ่งที่เหมาะสมเมื่อใช้หรือpipeasy_install


6
python-slugifyได้รับอนุญาตภายใต้ MIT แต่ใช้Unidecodeซึ่งได้รับอนุญาตภายใต้ GPL ดังนั้นจึงอาจไม่เหมาะกับบางโครงการ
Rotareti

@Rotareti คุณช่วยอธิบายให้ฉันฟังหน่อยได้ไหมว่าทำไมมันถึงไม่เหมาะกับโครงการทั้งหมด? เราไม่สามารถใช้สิ่งใดภายใต้ใบอนุญาต MIT หรือ GPL และรวมไว้ในซอฟต์แวร์เชิงพาณิชย์ได้หรือไม่? ฉันคิดว่าข้อ จำกัด เดียวคือการใส่ใบอนุญาตนอกเหนือจากรหัสที่เราพัฒนา ฉันผิดเหรอ?
Ghassem Tofighi

1
@GhassemTofighi ในระยะสั้น: คุณสามารถใช้ในซอฟต์แวร์เชิงพาณิชย์ของคุณได้ แต่ถ้าคุณใช้คุณต้องเปิดรหัสของคุณด้วยเช่นกัน อย่างไรก็ตาม IANAL และนี่ไม่ใช่คำแนะนำทางกฎหมาย
Rotareti

@GhassemTofighi อาจจะดูที่softwareengineering.stackexchange.com/q/47032/71504ในหัวข้อนั้น
kratenko

1
python-slugifyตอนนี้@Rotareti ใช้ค่าเริ่มต้นเป็น Artistic License'd text-unidecodeแทนที่จะเป็นลิขสิทธิ์ GPL Unidecodeเพื่อจัดการกับข้อกังวลในการออกใบอนุญาตของคุณ github.com/un33k/python-slugify/commit/…
Emilien

31

ติดตั้งแบบฟอร์ม Unidecode จากที่นี่เพื่อรองรับ Unicode

pip ติดตั้ง 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)

>>> ของฉันกำหนดเอง khello-vorld


1
สวัสดีมันค่อนข้างแปลก แต่มันให้ res ของฉันแบบนั้น "my-custom-ndud-d-d3-4-d2d3-4nd-d-"
derevo

1
@derevo ที่เกิดขึ้นเมื่อคุณไม่ส่งสตริง Unicode แทนที่slugify("My custom хелло ворлд")ด้วยslugify(u"My custom хелло ворлд")และควรใช้งานได้
kratenko

9
ฉันขอแนะนำให้ใช้ชื่อตัวแปรเช่นstr. สิ่งนี้จะซ่อนstrประเภทbuiltin
crodjer

2
Unidecode คือ GPL ซึ่งอาจไม่เหมาะสำหรับบางคน
Jorge Leitao

สิ่งที่เกี่ยวกับ reslugifying หรือ deslugifying
Ryan Chou

11

มีแพ็คเกจ python ชื่อawesome-slugify :

pip install awesome-slugify

ทำงานเช่นนี้:

from slugify import slugify

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

หน้า github ที่น่ากลัว slugify


2
แพ็คเกจดี! แต่ระวังมันได้รับอนุญาตภายใต้ GPL
Rotareti

1
โปรดทราบ: สิ่งนี้จะไม่อัตโนมัติ .lower () URL ของคุณ คุณจะต้องวิ่งslugify(text).lower()ถ้าคุณต้องการ
Kalob Taulien

7

มันทำงานได้ดีในDjangoดังนั้นฉันไม่เห็นว่าทำไมมันถึงไม่เป็นฟังก์ชัน slugify สำหรับวัตถุประสงค์ทั่วไป

คุณมีปัญหากับมันหรือไม่?


เป็นไปได้ว่าในบางกรณีมันเป็นความหวาดระแวงที่ดีต่อสุขภาพ :-)
nemesisfixx

รหัสย้ายมาที่นี่แล้ว
raylu

13
สำหรับคนขี้เกียจ:from django.utils.text import slugify
Spartacus

6

ปัญหาเกิดจากบรรทัดการปรับมาตรฐาน ascii:

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

เรียกว่ายูนิโคดนอร์มัลไลเซชันซึ่งจะไม่สลายอักขระจำนวนมากเป็น 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'ing เหล่านี้: 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)

เนื่องจากรูปแบบ[-]+ไม่แตกต่างจาก-+และคุณไม่สนใจที่จะจับคู่ยัติภังค์เพียงตัวเดียวเพียงสองตัวขึ้นไป

แต่แน่นอนว่านี่เป็นเรื่องเล็กน้อย


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.