我正在尝试识别基本案例和提供案例之间的差异。寻找一个图书馆告诉我百分比或类似的相似性。
例如:
我有10个不同的HTML页面。 *所有这些都是404响应,只有一行2行随机代码(例如当天的时间或报价)。
现在,当我提供一个新的404页面时,我想要一个类似于“%80”的结果,但是如果我提供另一个页面完全不同或相同的网站但内容完全不同,我应该得到一些“%20相似”的内容。
基本上我想做的是,当我得到一个新的回复时,我想确定新回复是否与我之前提供的这10个页面类似。
我正试图在.NET中解决这个问题,库或算法建议会很棒。
答案 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距离。
答案 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);
});