Определение, если 2 HTML-страницы похожи - PullRequest
3 голосов
/ 20 сентября 2008

Я пытаюсь определить различия между базовым случаем и предоставленным случаем. Ищите библиотеку, чтобы сказать мне сходство в процентах или что-то подобное.

Например:

У меня есть 10 разных HTML-страниц. * Все они - 404 ответа только с одной строкой случайного кода (например, время или цитата дня).

Теперь, когда я предоставляю новую страницу 404, я хочу вернуть результат, такой как "% 80", однако, если я предоставлю другую страницу совершенно другого или того же сайта, но совершенно другого контента, я должен получить что-то незначительное "% 20 похожее".

По сути, я хочу, чтобы, получив новый ответ, я хочу определить, похож ли новый ответ на эти 10 страниц, которые я предоставил ранее.

Я пытаюсь решить эту проблему в .NET, библиотека или алгоритм были бы хороши.

Ответы [ 7 ]

1 голос
/ 04 апреля 2012

Если вы хотите использовать строковое решение, вы можете сделать снимок, используя k-граммы (вы вычисляете всю строку длины k последовательных символов для обоих файлов, а затем выполняете расстояние по Джакарду на результирующих наборах). Это стандартный способ выполнения приблизительных запросов в мире БД.

Если вас больше интересует иерархическая информация, встроенная в html-файл (например, вы говорили о неизменяемом разделе), вы можете преобразовать ее в xhtml (для java у вас есть http://htmlcleaner.sourceforge.net/, Я не в. net, но я думаю, что есть несколько альтернатив для этого env), видя файл, сгенерированный как упорядоченное помеченное дерево, вы можете использовать pq-граммы (http://www.inf.unibz.it/~augsten/publ/tods10/ для бумаги и java-код) для оценки структурного сходства (pq-граммы дерево обобщение строковых к-грамм).

На данный момент, если вы хотите, вы можете выполнить сравнение на основе хеш-функции для листа, содержащего текст, или использовать k-граммы для этих листьев и структурное подобие на основе pq-граммы для остальных.

1 голос
/ 20 сентября 2008

Вместо использования инструмента сравнения вы можете использовать детектор копирования / вставки (cpd). Затем вы можете настроить порог того, насколько похожими должны быть файлы.

Кроме того, я использовал их в прошлом, чтобы выследить мошенников в школе.

Sam

0 голосов
/ 15 июня 2013

Вы можете использовать jqgram, реализацию аппроксимации расстояния редактирования дерева PQ-Gram, чтобы конкретно решить эту проблему, но вам нужно будет запустить Node.js, если вы не хотите портировать на C #. Порт должен быть довольно простым, хотя ... алгоритм не так уж и сложен. Красота в простоте.

https://github.com/hoonto/jqgram

В этом примере показан пример DOM vs cheerio, который показывает, как обращаться с дочерними элементами и метками, чтобы сгенерировать приблизительное расстояние редактирования дерева. В результате вы получите число от нуля до единицы, и это ваше процентное равенство. Но обратите внимание, что нулевое значение не обязательно указывает на идентичные деревья, это только означает, что они очень похожи. Вы можете достаточно легко сравнить DOM с DOM или Cheerio против Cheerio - или использовать анализ HTML, который использует Cheerio, вместо того, чтобы беспокоиться об использовании всей библиотеки (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);
});
0 голосов
/ 20 сентября 2008

базовый алгоритм, который я бы использовал:

парсит текстовое содержимое страниц с обеих сторон, старого и нового. по мере разбора следите за тем, сколько байтов было обработано для последующего использования, чтобы определить, сколько% изменилось. Теперь, когда у вас есть полная история на каждой стороне, создайте опорные точки одинаковости. Для каждой точки ахорности, которую вы имеете, попробуйте расширить это вперед и назад. Определите разницу между вашими ахорными очками одинаковости. Переберите все выявленные вами различия и суммируйте их количество байтов. вычислите свой процент различий, используя общее количество байтов разницы сумм и общий байт истории (тот, который вы рассчитали ранее).

0 голосов
/ 20 сентября 2008

для вашей задачи будет достаточно запустить утилиту diff командной строки и проанализировать результаты.

Это не разовая работа, мне нужно решение, интегрированное в приложение.

И у diff здесь свои проблемы, потому что я не могу сказать, чтобы diff обрабатывал 5 страниц и игнорировал биты, которые постоянно меняются.

Эти части могут быть большими, они могут постоянно менять 2 КБ стандартного текста. И я думаю, что с точки зрения различий это большое изменение, однако, с моей точки зрения, это просто изменение одного раздела (который, как известно, изменяется во всех остальных 9 файлах, поэтому следует полностью игнорировать).

Может быть, библиотека diff может сделать это, но я не знаю о такой библиотеке.

0 голосов
/ 20 сентября 2008

для вашей задачи будет достаточно запустить утилиту diff командной строки и проанализировать результаты.

В качестве альтернативы вам необходимо реализовать алгоритм LCS , но для меня это будет излишним.

0 голосов
/ 20 сентября 2008

Быстрый и грязный способ - вычислить расстояние Левенштейна в разметке.

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

...