对于我正在进行的web抓取项目,我计划将实体存储在数据库中,其中实体的ID是其名称/标题的md5哈希。
然而,由于字符串中存在Unicode,相同名称/标题的不同哈希将出现
例如;运动学、物理教育和娱乐;将不同于";运动学、物理教育和娱乐";。
我尝试使用Unicode规范化,但哈希之间的差异仍然是相同的
import hashlib
import unicodedata
def generate_id(*args):
"""
:param args: strings to be used to generate an id
:return: md5 hash of the passed arguments
"""
string = ''
for arg in args:
string += ' ' + arg
hash_algorithm = hashlib.md5()
hash_algorithm.update(string.encode('utf-8'))
return hash_algorithm.hexdigest()
def clean_text(text):
"""
normalizes the unicode in a text to be more readable and generate a more accurate id from
:param text: string to be normalized
:return: normalized version of text
"""
return unicodedata.normalize('NFC', text)
print(generate_id(clean_text('Kinesiology, Phys Edxa0and Recreation'))) # hashes to acd21f3b094a77d1a2393a8daeac42d9
print(generate_id('Kinesiology, Phys Ed and Recreation')) # hashes to 5ac6bc3ca3d743d99e9b93a7a5379fe9
我该怎么做才能确保两个字符串相同并散列到相同的id,使得"Kinesiology,Phys Ed \xA0 and Recreation"与"Kinesiology,Phys Ed\xA0 and Recreation"是相同的字符串和散列(无论存在哪种unicode,任何2个字符串都相同(?
由于"具有相同的散列";只是二进制相等的代理,您需要的是将字符串规范化为相同。
在Unicode术语中,给定的两个字符串在规范上不等价,但它们是兼容的。因此,您将能够使用clean_text()
函数中的兼容性分解/组合范式(NFKD
或NFKC
(生成相同的哈希:
def clean_text(text):
return unicodedata.normalize('NFKD', text)
NO-BREAK SPACE (U+00A0)
字符的分解特性被设置为<noBreak> SPACE (U+0020)
。事实上,分解属性中存在一个关键字(在本例中为<noBreak>
(,这表明该字符与正则空间字符兼容,但在规范上不等价。
旁注
因为它是在评论中要求的,所以对NFKC和NFKD范式之间的区别进行了一点澄清:
Unicode字符可以由多个代码点组成。某些字符可以用不同的方式(但在规范上等效(表示:要么作为单个代码点,要么作为代码点的组合。例如:é
可以表示为é
或e + ◌́
。当标准化时,合成正规形式(NFC,NFKC(将尝试将序列转换为其合成形式(e + ◌́
→é
(;分解范式(NFD,NFKD(将尝试将组合字符转换为序列(é
→e + ◌́
(。你使用哪一个完全取决于情况。只是要确保不要把苹果比作桔子。