You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:19:22