确定2个HTML页面是否相似

时间:2008-09-20 10:58:22

标签: .net diff similarity fuzzy

我正在尝试识别基本案例和提供案例之间的差异。寻找一个图书馆告诉我百分比或类似的相似性。

例如:

我有10个不同的HTML页面。 *所有这些都是404响应,只有一行2行随机代码(例如当天的时间或报价)。

现在,当我提供一个新的404页面时,我想要一个类似于“%80”的结果,但是如果我提供另一个页面完全不同或相同的网站但内容完全不同,我应该得到一些“%20相似”的内容。

基本上我想做的是,当我得到一个新的回复时,我想确定新回复是否与我之前提供的这10个页面类似。

我正试图在.NET中解决这个问题,库或算法建议会很棒。

7 个答案:

答案 0 :(得分:1)

您可以使用复制/粘贴检测器(cpd),而不是使用diff工具。然后,您可以配置您希望文件的相似程度的阈值。

顺便说一下,我过去曾用这些来追查学校里的骗子。

萨姆

答案 1 :(得分:1)

如果你想使用基于字符串的解决方案,你可以使用k-gram进行一次拍摄(你计算两个文件的连续字符长度k的所有字符串,然后在结果集上执行Jaccard距离)。它是在DB world中执行近似查询的标准方法。

如果您对嵌入到html文件中的分层信息感兴趣(例如,您正在谈论一个不可变的部分),您可以将其转换为xhtml(对于您有http://htmlcleaner.sourceforge.net/的java,我不会。 net,但我认为这个env也有几种选择),看到生成的文件是有序的标记树,你可以使用pq-gram(http://www.inf.unibz.it/~augsten/publ/tods10/用于纸张和java代码)来评估结构相似性(pq-gram是字符串k-gram的树推广)。

此时,如果您需要,您可以对包含文本的叶子执行基于哈希的比较,或者对此叶子使用k-gram,并为其余叶子使用基于结构pq-gram的相似性。

答案 2 :(得分:0)

快速而肮脏的方法是计算标记的Levenshtein距离。

http://en.wikipedia.org/wiki/Levenstein_distance

答案 3 :(得分:0)

对于您的任务,运行命令行diff实用程序并分析结果就足够了。

或者你需要实现一个LCS算法,但对我来说这将是一个过度杀伤。

答案 4 :(得分:0)

  

对于你的任务来说就足够了   运行命令行diff实用程序和   分析结果。

这不是一次性的工作,我需要一个集成到应用程序中的解决方案。

并且差异在这里有它自己的问题,因为我不能告诉diff处理5页并忽略那些不断变化的位。

这些部分可以很大,它可以2kb的标准文本不断变化。我认为从不同的角度来看,这是一个很大的变化,但从我的观点来看,它只是一个部分的变化(已知在所有其他9个文件中都有变化,因此应该完全忽略)。

也许差异库可以做到这一点,但我不知道这样的库。

答案 5 :(得分:0)

我将使用的基本算法:

解析双方的页面文本内容,旧的和新的。在您解析时,跟踪已处理的字节数,以便稍后使用以确定已更改的百分比。现在你已经在每一方都有完整的故事,建立相同的锚点。对于你所拥有的每一个相同点,尝试向前和向后展开。确定您的同一性点之间的差异作为差异。循环遍历您已识别的每个差异差距并总结其字节数。通过使用总ammount差异字节数和故事的总字节数(您之前计算的那个)计算您的差异百分比。

答案 6 :(得分:0)

您可以使用jqgram,PQ-Gram树编辑距离近似的实现来专门解决此问题,但如果您不想移植到C#,则需要运行Node.js。虽然端口应该很简单......算法并不是那么复杂。简约之美。

https://github.com/hoonto/jqgram

在示例中是DOM vs cheerio示例,其中显示了如何处理子项和标签以生成近似树编辑距离。它会为您提供一个介于0和1之间的数字,因此这是您的百分比相等。但请注意,值为零并不一定表示相同的树,它只表示它们非常相似。你可以轻松地完成DOM vs DOM比较或Cheerio vs Cheerio - 或者使用Cheerio使用的HTML解析而不是担心使用整个库(开箱即用的Cheerio是一个相当快速的服务器端jQuery-和类似DOM的实现)。

显然,这个解决方案是Node.js和浏览器javascript特定的,但我认为这些挑战可能比移植到C#/ .NET更容易。

// This could probably be optimized significantly, but is a real-world
// example of how to use tree edit distance in the browser.

// For cheerio, you'll have to browserify, 
// which requires some fiddling around
// due to cheerio's dynamically generated 
// require's (good grief) that browserify 
// does not see due to the static nature 
// of its code analysis (dynamic off-line
// analysis is hard, but doable).
//
// Ultimately, the goal is to end up with 
// something like this in the browser:

var cheerio = require('./lib/cheerio'); 

// The easy part, jqgram:
var jq = require("../jqgram").jqgram;

// Make a cheerio DOM:
var html = '<body><div id="a"><div class="c d"><span>Irrelevent text</span></div></div></body>';

var cheeriodom = cheerio.load(html, {
    ignoreWhitespace: false,
    lowerCaseTags: true
});

// For ease, lets assume you have jQuery laoded:
var realdom = $('body');

// The lfn and cfn functions allow you to specify
// how labels and children should be defined:
jq.distance({
    root: cheeriodom,
    lfn: function(node){ 
        // We don't have to lowercase this because we already
        // asked cheerio to do that for us above (lowerCaseTags).
        return node.name; 
    },
    cfn: function(node){ 
        // Cheerio maintains attributes in the attribs array:
        // We're going to put id's and classes in as children 
        // of nodes in our cheerio tree
        var retarr = []; 
        if(!! node.attribs && !! node.attribs.class){
            retarr = retarr.concat(node.attribs.class.split(' '));
        }
        if(!! node.attribs && !! node.attribs.id){
            retarr.push(node.attribs.id);
        }
        retarr = retarr.concat(node.children);
        return  retarr;
    }
},{
    root: realdom,
    lfn: function(node){ 
        return node.nodeName.toLowerCase(); 
    },
    cfn: function(node){ 
        var retarr = [];
        if(!! node.attributes && !! node.attributes.class && !! node.attributes.class.nodeValue){
            retarr = retarr.concat(node.attributes.class.nodeValue.split(' '));
        }
        if(!! node.attributes && !! node.attributes.id && !! node.attributes.id.nodeValue) {
            retarr.push(node.attributes.id.nodeValue);
        }
        for(var i=0; i<node.children.length; ++i){
            retarr.push(node.children[i]);
        }
        return retarr;
    }
},{ p:2, q:3, depth:10 },
function(result) {
    console.log(result.distance);
});
相关问题