如何让MySQL不区分相似字符串?解决多源电影标题存储差异问题
解决MySQL中电影标题细微差异匹配问题(替代Soundex方案)
嘿,针对你爬取电影信息时遇到的标题细微差异匹配难题——比如Kill Bill: Vol 2 vs Kill Bill: Vol II、Padmaavat vs Padmavat这类情况,Soundex确实对非英文支持拉胯,这里有几个更实用的替代方案:
1. 用Levenshtein距离判断相似性
Levenshtein距离说白了就是两个字符串之间的“编辑步数”(插入、删除、替换字符的次数),步数越少,字符串越相似。MySQL默认没带这个函数,你可以自己创建一个:
DELIMITER // CREATE FUNCTION levenshtein(s1 VARCHAR(255), s2 VARCHAR(255)) RETURNS INT DETERMINISTIC BEGIN DECLARE s1_len, s2_len, i, j, c, c_temp INT; DECLARE s1_char CHAR; DECLARE cv0, cv1 VARBINARY(256); SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2); IF s1_len = 0 THEN RETURN s2_len; END IF; IF s2_len = 0 THEN RETURN s1_len; END IF; SET cv0 = 0x00; FOR i FROM 1 TO s2_len DO SET cv0 = CONCAT(cv0, UNHEX(HEX(i))); END FOR; FOR i FROM 1 TO s1_len DO SET s1_char = SUBSTRING(s1, i, 1); SET cv1 = UNHEX(HEX(i)); SET j = 1; WHILE j <= s2_len DO SET c = IF(s1_char = SUBSTRING(s2, j, 1), 0, 1); SET c_temp = CONV(HEX(SUBSTRING(cv0, j, 1)), 16, 10) + c; SET c_temp = LEAST( CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + 1, c_temp, CONV(HEX(SUBSTRING(cv0, j+1, 1)), 16, 10) + 1 ); SET cv1 = CONCAT(cv1, UNHEX(HEX(c_temp))); SET j = j + 1; END WHILE; SET cv0 = cv1; END FOR; RETURN CONV(HEX(SUBSTRING(cv0, s2_len+1, 1)), 16, 10); END // DELIMITER ;
使用时设定一个合理的阈值(比如编辑距离≤2就认为是同一部电影):
SELECT * FROM movies WHERE levenshtein(title, 'Kill Bill: Vol 2') <= 2;
2. 先标准化标题再存储/匹配
最直接的思路是提前把所有标题“归一化”,消除常见差异后再入库,这样匹配时直接比对标准化后的字符串就行:
- 统一数字格式:把罗马数字
II/III转成阿拉伯数字2/3,比如Vol II→Vol 2 - 清理冗余字符:去掉标点、特殊符号,统一转成小写(比如
Kill Bill: Vol 2→kill bill vol 2) - 处理已知拼写变体:维护一个映射表,把
Padmavat和Padmaavat这类变体映射成同一个标准值
你可以在爬虫端做标准化,也可以在MySQL里写个函数:
CREATE FUNCTION normalize_title(title VARCHAR(255)) RETURNS VARCHAR(255) DETERMINISTIC BEGIN -- 转小写 SET title = LOWER(title); -- 替换罗马数字为阿拉伯数字 SET title = REPLACE(title, ' ii ', ' 2 '); SET title = REPLACE(title, ' iii ', ' 3 '); -- 移除非字母数字和空格的字符 SET title = REGEXP_REPLACE(title, '[^a-z0-9 ]', ''); -- 把连续空格换成单个空格 SET title = REGEXP_REPLACE(title, ' +', ' '); RETURN TRIM(title); END;
之后入库时可以同时存原始标题和标准化标题,匹配时用标准化字段:
SELECT * FROM movies WHERE normalize_title(title) = normalize_title('Kill Bill: Vol II');
3. 用ngram全文索引适配多语言
MySQL的ngram全文分词器对非英文(比如中文、印地语)友好,也能识别英文的拼写变体。先给表创建ngram全文索引:
CREATE TABLE movies ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), FULLTEXT INDEX ft_title (title) WITH PARSER ngram ) ENGINE=InnoDB CHARACTER SET utf8mb4;
然后用MATCH ... AGAINST做模糊匹配,还能按相关性排序:
SELECT *, MATCH(title) AGAINST('Padmaavat' IN BOOLEAN MODE) AS relevance FROM movies WHERE MATCH(title) AGAINST('Padmaavat' IN BOOLEAN MODE) ORDER BY relevance DESC;
4. 爬虫端用外部工具提前处理
如果MySQL端的处理不够灵活,你可以在爬虫抓取数据时,用Python的第三方库先做相似性校验:
- 用
fuzzywuzzy计算字符串相似度(比如设定相似度≥90%就认为是同一部) - 用
difflib做序列匹配 - 针对非英文标题,用语言特定的工具做分词或标准化(比如印地语的拼写归一化工具)
这样在入库前就能判断是否已经存在相似记录,避免重复存储。
内容的提问来源于stack exchange,提问作者Prashant Pandey
相关产品推荐
相关产品推荐

