Идентификация изменений HTML-структур, приведенных к формату JSON Довбенко А. В.
Довбенко Алексей Викторович /Dovbenko Alexey Victorovich - аспирант, кафедра теоретических основ информатики, факультет прикладной математики, информатики и механики,
Воронежский государственный университет, г. Воронеж
Аннотация: в работе произведен анализ идентификации изменений в структуре типа JSON, с возможностью хранить версии изменений для возможного просмотра старых данных, а также возможностью анализировать и реагировать на частоту того или иного изменения.
Ключевые слова: базы данных, NoSQL, MongoDB, JSON, версионность, аналитика.
С момента появления первых поисковых систем появилась задача обновлять данные считанных с сайтов, по умолчанию использовалась стандартная схема, поисковый робот заходил на сайт через определенный промежуток времени и обновлял данные, но со временем интернет-аудитория расширялась, сайтов становилось больше, некоторое из них стали набирать популярность, некоторое же, наоборот, имели крайне малую аудиторию и почти не обновлялись, стало понятно, что для каждого сайта интервал обновления должен быть разным. В этой статье предложена схема идентификации частоты и важности изменений, с помощью которой можно строить автоматическую систему сканирования сайтов.
Как известно, одна из основных задач парсинга сайта поисковыми системами - идентификация изменений на странице. После того как мы получили контент сайта и очистили его от ненужной шелухи (тэгов, классов, атрибутов, скриптов, стилей), контент хэшируется, пусть в данном случае алгоритмом хэширования MD5 (алгоритм не столь важен, так как в данной статье мы не рассматриваем вероятность коллизий на n-число хэшированных объектов), и присваиваем данному хэшу адрес страницы, время сканирования и полный контент страницы, т. е. структура в базе данных (для данного эксперимента использовалась MongoDB, так как данные будут не структурированы) выглядит примерно так (Рисунок 1):
4
a: Obj ectld( "563 e271916aOdflc&fO£7S21"} {5 fields) Object
Q Jd Objectld("563e271916aOdfl СЙ087321 “) Objectld
in urf httpsr//www.google.ru/ String
Ш hash cc9ed9aff4f52£064 db3esf d6dee5e7d String
П content <!doctype html> <btml rtemscoper”* rtemtype=‘"ht... String
“ time 2015-11-10 04:0ОЮО.СОО1 Date
Рис. 1. Пример хранения html страницы
При последующем скане страницы google.ru будет сравниваться хэш сумма нынешнего скана с предыдущим, тем самым будет решаться - парсить страницу заново или нет. Но на каждом сайте есть статические блоки (header, footer, меню) и блоки с разной частотой обновления, как, например, на новостном сайте новость может дополняться в течении дня или же не дополняться вообще, в то время как комментарии к ней могут появляться каждую минуту на протяжении недели, также стоит учитывать, что многие сайты показывают динамическую информацию, которая зачастую не нужна поисковой машине (текущее время и дата, время загрузки страницы и т. п.). Соответственно данный метод хранения изменений не идеален, так как мы не можем быстро обнаружить изменения конкретного блока, и есть большая вероятность выполнения ненужной работы и частого сканирования сайта, где информация не меняется вообще.
Соответственно, надо хранить хэши блоков, так мы сможем довольно быстро обнаружить изменения и уже потом решать, что делать, тем самым уменьшая объем хранимых данных. Описание метода парсера, который нормализует и структурирует сайт по определенным блокам, заслуживает отдельной объемной статьи по интеллектуальной системе парсинга, поэтому тут мы пропустим этот шаг. В сущности, нам надо привести сайт к документу json по структуре типа (Рисунок 2):
Рис. 2. Шаблон для разбивки html страницы на блоки
В данной структуре блок head будет иметь свой хэш, блок body - свой, причем каждый дочерний элемент блока body также будет иметь свой хэш, соответственно сравнивание хэшей будет идти по древовидной схеме, пока не найдутся измененные блоки. Итак, испробуем это на практике. Как вы заметили, блок «head» имеет другую структуру дочерних блоков, в отличие от блока «body», сделано это потому, что реакция от изменений в блоке head и блока body кардинально отличается, поэтому их лучше рассматривать отдельно.
К нормализованной блочной структуре приходят многие поисковые системы, например, Google. Основной принцип — это использование микроразметки, которой обозначается важная часть информации для показа в превью в списке результатов поиска [1].
Возьмем стандартную архитектуру сайта в упрощенном виде (Рисунок 3):
Header
Blockl
link 1 link 2 Block2
1шк з Block-4
Comment 1
Conmient2
Footer
Рис. 3. Типичная структура сайта
Коллекции для такого документа будут иметь следующий вид (Рисунок 4):
a: 0t>jectld("56413809siff395006000033") (5 fields} Object
U .id ObjectId("56413SWi2ff3S500600W33") Objectld
111) link http://apsay.dev String
“ data.se an 2015-11-10 00:1*21,000Z Date
U hash cd59S36eB674caOfO0eb6bf5O16e270e String
CD content «htmi* «head* «title*Test*/title* «mete names "k,,, String
Рис. 4. Основная коллекция с актуальными данными для быстрого доступа
И коллекция элементов этой страницы (Рисунок 5):
^ ю PO Gbj ectld ("564138Q9a2ff395QG6000034 "5
|__I Jd
D contentjd
J О head
Q title Q keywords Q description О hash ©_i last_update C*D count_update a О body ^ О static
J О header
E3 content
EZD hash
Iast_update Ш count_update
> О menu
> О footer Lilli hash
®_j last_update Ш count_update J О content J О Ыоск_1
EZD content Ell hash <r!_■ last_update И count_update J О Ыоск_2
Ell content Ell hash ©_> last_update C*D countjjpdate
> О Ыоск_3 0_i lastjjpdate l#J count_update
{4 fields}
Gbj ectld ("564130O9a2ff395OO6OOOO34"] Gbj ectld ("56413809a2ff395006000033" J {6 fields}
Test
test, testl This is test
eB1570bQ67cacaf7deOQc095ad5376e6 2015-11-10 00:19:21,000Z 0
{2 fields}
{6 fields}
{4 fields}
Header
bf50 d5e№1106d0 abe925 af3 c2e&f7 e7 2015-11-10 00:19:21,000Z 0
{4 fields}
{4 fields}
a51d0429709fbadc573Oc24472d562f7 2015-11-10 00:19:21.QQQZ 0
{5 fields}
{ 4 fields }
Blockl
b5e25dl9bfd457de6065955f0f4ac6ac 2015-11-10 00:19:21.0QQZ 0
{4 fields}
Block2
3fbe9f2beae8654bS6e2d9570f6.3d4e2 2015-11-10 00:19:21,000Z 0
{4 fields}
2015-11-10 Q0:19:21,Q0QZ 0
Object
Objected
Obj ectld
Object
String
String
String
String
Date
Int32
Object
Object
Object
String
String
Date
Int32
Object
Object
String
Date
Int32
Object
0 bj ect
String
String
Date
Int32
Object
String
String
Date
Int32
Object
Date
Int32
Рис. 5. Коллекция блоков html страницы, с частотой обновления блоков
Теперь рассмотрим ситуацию обновления нашего документа: меняется контент блока 3. Мы при скане страницы рекурсивно сравниваем старые блоки с новыми по хэшу, если хэш совпадает, то пропускаем блок, если нет, то обновляем данные (хэш, дата обновления и количество обновлений), в обновленном блоке также заменяем контент.
Что этот подход в итоге дает? По сути дела, это прослойка между сканом страницы и парсингом информации, которая дает определенные плюсы, а именно:
1) Упрощает работу получения информации, работать с конкретным измененным блоком намного проще и быстрей чем со всей страницей.
2) Мы имеем некоторую статистику по изменяемости сайта, благодаря которой можем обнаруживать подозрительную активность там, где её раньше не было (например, меню стало обновляться в разы чаще).
3) В зависимости от статистических данных можем по-разному реагировать на изменение определенных блоков.
4) Новая отрасль в развитии поиска сайтов дубликатов и, возможно, влияние на сам алгоритм ранжирования, если рассматривать каждый блок как отдельный источник информации со своими ключевыми словами.
Но также не будем и забывать про минусы, а именно:
1) Такая система будет эффективна только с хорошим парсером, который будет автоматически разбивать сайт на блоки, причем, чем больше уровень вложенности у блоков, тем лучше будет работать система.
2) Дополнительные затраты по памяти, но сейчас стоимость таких ресурсов относительно дешевая, плюс, мы можем отказаться от хранения самой информации, а хранить только хэш, тем самым сэкономив приличное количество места.
Так или иначе, данная структура вполне имеет право на существование, причем не только в поисковых машинах, но и при работе с различными api, где преобразовывать данные изначально не надо, так как они приходят уже структурированные, соответственно можно существенно упростить работу по хранению статистики изменений сложных структур.
Хотелось бы отметить, что похожую структуру успешно используют пакетные менеджеры, такие как composer, nmp (для PHP и NodeJS соответственно), для подтягивания зависимостей того или иного пакета [2,
3].
Литература
1. Structured Data https: [Электронный ресурс]. Режим доступа: //developers.google.com/structured-data/rich-snippets/products.
2. Dependency management [Электронный ресурс]. Режим доступа: https://getcomposer.org/doc/00-
intro.md#dependency-management.
3. What is npm? [Электронный ресурс]. Режим доступа: https: //docs.npmjs.com/getting-started/what-is-npm.