Python中的字符串slugification

Posted

技术标签:

【中文标题】Python中的字符串slugification【英文标题】:String slugification in Python 【发布时间】:2011-07-31 06:00:22 【问题描述】:

我正在寻找“slugify”字符串what "slug" is的最佳方法,我目前的解决方案是基于this recipe

我稍微改了一下:

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 吗?如果是这样,如果将 unicode() 包裹在最后一个 re.sub 可能会更好,这就是 django 所做的。此外, [^a-z0-9]+ 可以缩短为使用 \w 。请参阅 django.template.defaultfilters,它与您的接近,但更精致。 URL 中是否允许使用 unicode 字符?另外,我已将 \w 更改为 a-z0-9,因为 \w 包含 _ 字符和大写字母。字母是预先设置为小写的,所以不会有大写字母匹配。 '_' 是有效的(但你的选择,你确实问过),unicode 是百分比编码的字符。 谢谢迈克。好吧,我问了一个错误的问题。如果我们已经替换了除“a-z”、“0-9”和“-”之外的所有字符,是否有任何理由将其编码回 unicode 字符串? 对于 django,我相信将所有字符串都作为 unicode 对象以实现兼容性对他们来说很重要。如果您愿意,这是您的选择。 【参考方案1】:

有一个名为 python-slugify 的 python 包,它在 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")

见More examples

这个包比你发布的要多一点(看看源代码,它只是一个文件)。该项目仍然有效(在我最初回答前 2 天更新,七年后(最后一次检查 2020-06-30),它仍然在更新)。

小心:还有第二个包,名为slugify。如果您同时拥有它们,您可能会遇到问题,因为它们具有相同的导入名称。刚刚命名为slugify 的那个并没有完成我快速检查的所有操作:"Ich heiße" 变成了"ich-heie"(应该是"ich-heisse"),所以在使用pipeasy_install 时一定要选择正确的那个.

【讨论】:

python-slugify 在 MIT 下获得许可,但它使用在 GPL 下获得许可的Unidecode,因此它可能不适合某些项目。 @Rotareti 你能解释一下为什么它不能适合所有项目吗?我们不能在 MIT 或 GPL 许可下使用任何东西并将它们包含在商业软件中吗?我认为唯一的限制是将许可证放在我们开发的代码之外。我错了吗? @GhassemTofighi 简而言之:你可以在你的商业软件中使用它,但如果你使用它,你也必须开源你的代码。无论如何,IANAL,这不是法律建议。 @GhassemTofighi 或许可以看看 softwareengineering.stackexchange.com/q/47032/71504 关于该主题 @Rotareti python-slugify 现在默认为艺术许可的text-unidecode,而不是 GPL 许可的Unidecode,解决了您的许可问题。 github.com/un33k/python-slugify/commit/…【参考方案2】:

安装 unidecode 表单 from here 以获得 unicode 支持

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

【讨论】:

嗨,它有点奇怪,但它为我的资源提供了“my-custom-ndud-d-d3-4-d2d3-4nd-d-” @derevo 发生在您不发送 unicode 字符串时。将slugify("My custom хелло ворлд") 替换为slugify(u"My custom хелло ворлд"),它应该可以工作。 我建议不要使用像str 这样的变量名。这隐藏了内置的 str 类型。 unidecode 是 GPL,可能不适合某些人。 那reslugifying或deslugifying怎么样。【参考方案3】:

有一个名为awesome-slugify的python包:

pip install awesome-slugify

像这样工作:

from slugify import slugify

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

awesome-slugify github page

【讨论】:

不错的包装!但要小心,它是在 GPL 下获得许可的。 注意:这不会自动 .lower() 你的网址。如果需要,您需要运行 slugify(text).lower()【参考方案4】:

它在 Django 中运行良好,所以我不明白为什么它不是一个好的通用 slugify 函数。

你有什么问题吗?

【讨论】:

有可能,在某些情况下,这是一种健康的偏执狂:-) 代码已移至here。 懒人:from django.utils.text import slugify【参考方案5】:

问题在于 ascii 规范化行:

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

它被称为unicode normalization,它不会将大量字符分解为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)

这是 django.utils.text 中的 slugify 函数 这应该可以满足您的要求。

【讨论】:

【参考方案7】:

Unidecode 不错;但是,请注意:unidecode 是 GPL。如果此许可证不适合,请使用this one

【讨论】:

【参考方案8】:

GitHub 上的几个选项:

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

每个 API 支持的参数略有不同,因此您需要仔细研究以确定您喜欢什么。

特别要注意它们为处理非 ASCII 字符提供的不同选项。 Pydanny 写了一篇非常有用的博文,说明了这些 slugify 库中的一些 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

【讨论】:

【参考方案9】:

你可以考虑把最后一行改成

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

因为模式[-]+-+ 没有什么不同,而且你并不关心只匹配一个连字符,只匹配两个或更多。

但是,当然,这是非常轻微的。

【讨论】:

【参考方案10】:

另一个选项是boltons.strutils.slugify。 Boltons 还有很多其他有用的功能,并且是在 BSD 许可证下分发的。

【讨论】:

【参考方案11】:

以您的示例为例,一种快速的方法可能是:

s = 'String to slugify'

slug = s.replace(" ", "-").lower()

【讨论】:

以上是关于Python中的字符串slugification的主要内容,如果未能解决你的问题,请参考以下文章

python3.8保留字总数

在 Python 中匹配 Unicode 字边界

python之struct详解

python之struct详解

python中,如何去掉字串自带的引号

Python3 基础语法:编码标识符python保留字注释多行语句等介绍