在Python中使用NLTK查找押韵

21 投票
4 回答
17438 浏览
提问于 2025-04-20 08:19

我有一首诗,想用Python代码来打印出那些押韵的词。

到目前为止,我已经完成了:

  1. wordpunct_tokenize()把诗句分开。
  2. 清理这些词,去掉标点符号。
  3. 把每句诗的最后一个词存到一个列表里。
  4. cmudict.entries()生成另一个列表,里面包含这些最后一个词及其发音。

我现在卡在了下一步。应该怎么匹配这些发音呢?总的来说,我的主要任务是判断两个给定的词是否押韵。如果押韵,就返回True,否则返回False

4 个回答

1

虽然来得有点晚,但我在做一个项目,目的是找到很多押韵的句子来处理。我开始是在这个问题讨论串里,受到kender的认可回答的启发。值得知道的是,当你要找成千上万的押韵时,可以利用字典查找来大大提高速度。

这个内容也更新到了python 3.x。

把CMU发音字典存成一个json文件,并写一个初始化函数和一个把条目从元组转换成字典的函数。

# global
json_entries = None

def tup2dict(tup, di):
for a, b in tup:
    di.setdefault(a, []).append(b)
return di

def init_cmu(args):
    import nltk
    nltk.download('cmudict')
    nltk.corpus.cmudict.ensure_loaded()
    cmu_entries = nltk.corpus.cmudict.entries()
    cmu_dict = dict()
    tup2dict(cmu_entries, cmu_dict)
    with open('./maps/cmu.json', 'w') as convert_file:
        convert_file.write(json.dumps(cmu_dict))

如果你的脚本中有一个需要判断押韵的函数,可以在脚本的开头调用这个函数,以确保你可以访问到字典。

def require_rhyme_dict():
    global json_entries
    if json_entries:
        return
    try:
        jsonf = open('./maps/cmu.json', 'r')
    except:
        pass
    else:
        # Global
        json_entries = dict(json.load(jsonf))
        jsonf.close()
        print('json_entries loaded.')

最后,这里有一个修改过的kender的押韵函数,使用了字典和一种不同的子词检查方法(我遇到过一个边缘情况,押韵的词不是“lame”,但根据单词的起始索引和长度,这些词是不允许押韵的。具体例子我记不太清了,反正很少见)。

当连续调用几千次时,下面这个函数的调用速度比遍历所有nltk cmu条目来找到发音音节的列表要快得多。一如既往,level指的是两个词之间的发音音节数量,只有当这些音节在结尾处匹配时,才能算作押韵。在这个例子中,如果一个词有多种发音,也会对每个词进行检查。

def isContainSameWord(word1, word2):
    if word1 in word2 or word2 in word1:
        return True
    else:
        return False

def isRhyme(word1, word2, level):
    require_rhyme_dict()
    global json_entries
    if isContainSameWord(word1, word2):
        return False
    word1_syllable_arrs = json_entries.get(word1)
    word2_syllables_arrs = json_entries.get(word2)
    if not word1_syllable_arrs or not word2_syllables_arrs:
        return False
    for a in word1_syllable_arrs:
        for b in word2_syllables_arrs:
            if a[-level:] == b[-level:]:
                return True
    return False
2

可以使用soundex或double metaphone这两种方法来判断单词是否押韵。NLTK这个库好像没有直接实现这些功能,不过你可以在网上快速搜索一下,会找到一些相关的实现。

20

Pronouncing库在这方面表现得非常好。它不需要复杂的操作,加载速度快,而且是基于CMU发音词典,所以很可靠。

https://pypi.python.org/pypi/pronouncing

来自他们的文档

>>> import pronouncing
>>> pronouncing.rhymes("climbing")
['diming', 'liming', 'priming', 'rhyming', 'timing']
14

在这里,我找到了一种使用NLTK来查找与给定单词押韵的方法:

def rhyme(inp, level):
     entries = nltk.corpus.cmudict.entries()
     syllables = [(word, syl) for word, syl in entries if word == inp]
     rhymes = []
     for (word, syllable) in syllables:
             rhymes += [word for word, pron in entries if pron[-level:] == syllable[-level:]]
     return set(rhymes)

其中,inp是一个单词,而level表示押韵的好坏程度。

所以你可以使用这个函数来检查两个单词是否押韵,只需查看其中一个单词是否在另一个单词的允许押韵列表中:

def doTheyRhyme(word1, word2):
    # first, we don't want to report 'glue' and 'unglue' as rhyming words
    # those kind of rhymes are LAME
    if word1.find(word2) == len(word1) - len(word2):
        return False
    if word2.find(word1) == len(word2) - len(word1): 
        return False

    return word1 in rhyme(word2, 1)

撰写回答