Я никогда по работе не сталкивался с задачами получения данных c сайта или из html-документа. А тут появилась "личная" задача, решил попробовать.
У меня есть сын, после рождения которого мы с супругой по мере возможности писали в блог, расположенный на сайте diary.ru, всё, что с ним и нами происходило. Временной период - 10 лет. И нам захотелось сделать из этого книгу в малотиражной типографии. Сначала супруга вручную начала переносить записи в Ворд, но, во-первых, яжпрограммист - можно же автоматизировать, а во-вторых, хотелось попробовать несколько макетов, а с Вордом это проблематично.
Почитав Инфостарт и другие интернеты, я понял, что общий принцип одинаковый, но из-за того, что каждый сайт по своему уникален, написать более-менее автоматический алгоритм не получится. В конфигурации, которая приложена к публикации, я хотел сделать универсальный механизм с настройкой получаемых данных, но убедившись, что даже на самом сайте можно сделать разные настройки для своего дневника, бросил эту затею и сделал по конкретную разметку. Хотя, универсальность всё же присутствует.
В самом парсере вводится самая первая страница, с которой нужно начать процесс, общее количество страниц дневника (если будет введено больше, чем нужно - не беда), ну и количество постов на странице, т.к. оно может быть разным.
Уже после того, как всё было готово, все записи были в конфигурации, я попробовал спарсить несколько случайных дневников. Если структура не сильно отличается от структуры нашего дневника, парсер работает и для других дневников diary.ru.
При парсинге сохраняется разметка, форматирование (жирный, курсив, offtop), сохраняются картинки.
Для хранения форматированного текста я воспользовался данной статьёй //infostart.ru/public/273123/
Для загрузки картинок идеей из статьи //infostart.ru/public/257266/
Сам парсер построен на объекте ПостроительDOM. Текст процедуры в спойлере.
&НаКлиенте
Процедура ПарситьКлиент()
МассивТригеров = Новый Массив;
МассивТригеров.Добавить("countSecondDate postDate uline"); // дата
МассивТригеров.Добавить("postTitle header"); // время
МассивТригеров.Добавить("authorName"); // автор
МассивТригеров.Добавить("postTitle header"); // заголовок поста
МассивТригеров.Добавить("postInner"); // содержание поста
ЧтениеХТМЛ = Новый ЧтениеHTML;
ЧтениеХТМЛ.ОткрытьФайл(ВебСтраница, "UTF-8");
ПостроительДОМ = Новый ПостроительDOM;
ДокументХТМЛ = ПостроительДОМ.Прочитать(ЧтениеХТМЛ);
НоваяСтрока = Неопределено;
ДатаПоста = "";
ЭлементыДОМ = ДокументХТМЛ.ПолучитьЭлементыПоИмени("div");
Для каждого ТекЭлемент Из ЭлементыДОМ Цикл
ИмяКласса = ТекЭлемент.ИмяКласса;
Если НЕ МассивТригеров.Найти(ИмяКласса) = Неопределено Тогда
// дата
Если ИмяКласса = "countSecondDate postDate uline" Тогда
Если НоваяСтрока = Неопределено Тогда
НоваяСтрока = СобранныйКонтент.Добавить();
КонецЕсли;
ДочерниеУзлы = ТекЭлемент.ДочерниеУзлы;
Для каждого ТекУзел Из ДочерниеУзлы Цикл
Если ТекУзел.ИмяУзла = "span" Тогда
ДатаПоста = ТекУзел.ТекстовоеСодержимое;
НоваяСтрока.Дата = ДатаПоста;
НоваяСтрока.ИсходнаяСтраница = ВебСтраница;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// время
Если ИмяКласса = "postTitle header" Тогда
Если НоваяСтрока = Неопределено Тогда
НоваяСтрока = СобранныйКонтент.Добавить();
НоваяСтрока.Дата = ДатаПоста;
НоваяСтрока.ИсходнаяСтраница = ВебСтраница;
КонецЕсли;
ДочерниеУзлы = ТекЭлемент.ДочерниеУзлы;
Для каждого ТекУзел Из ДочерниеУзлы Цикл
Если ТекУзел.ИмяУзла = "span" Тогда
ВремяПоста = ТекУзел.ТекстовоеСодержимое;
НоваяСтрока.ВремяПоста = ВремяПоста;
НоваяСтрока.ИсходнаяСтраница = ВебСтраница;
КонецЕсли;
КонецЦикла;
КонецЕсли;
//Заголовок поста
Если ИмяКласса = "postTitle header" Тогда
ДобавитьНовуюСтроку(ДатаПоста, НоваяСтрока);
ДочерниеУзлы = ТекЭлемент.ДочерниеУзлы;
Для каждого ТекУзел Из ДочерниеУзлы Цикл
Если ТекУзел.ИмяУзла = "h2" Тогда
аДочерниеУзлы = ТекУзел.ДочерниеУзлы;
Для каждого аТекЭлемент Из аДочерниеУзлы Цикл
Если аТекЭлемент.ИмяУзла = "a" Тогда
НоваяСтрока.ЗаголовокПоста = аТекЭлемент.ТекстовоеСодержимое;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// автор
Если ИмяКласса = "authorName" Тогда
ДобавитьНовуюСтроку(ДатаПоста, НоваяСтрока);
ДочерниеУзлы = ТекЭлемент.ДочерниеУзлы;
Для каждого ТекУзел Из ДочерниеУзлы Цикл
Если ТекУзел.ИмяУзла = "a" Тогда
аДочерниеУзлы = ТекУзел.ДочерниеУзлы;
Для каждого аТекЭлемент Из аДочерниеУзлы Цикл
Если аТекЭлемент.ИмяУзла = "strong" Тогда
НоваяСтрока.Автор = аТекЭлемент.ТекстовоеСодержимое;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// содерждание поста
Если ИмяКласса = "postInner" Тогда
ДобавитьНовуюСтроку(ДатаПоста, НоваяСтрока);
ДочерниеУзлы = ТекЭлемент.ДочерниеУзлы;
Для каждого ТекУзел Из ДочерниеУзлы Цикл
Если ТекУзел.ИмяУзла = "div" Тогда
дДочерниеУзлы = ТекУзел.ДочерниеУзлы;
Для каждого дТекУзел Из дДочерниеУзлы Цикл
Если дТекУзел.ИмяУзла = "div" Тогда
СодержаниеПоста = "<body><p>";
ДочерниеУзлыГлубокого = дТекУзел.ДочерниеУзлы;
Для каждого ТекСтрока Из ДочерниеУзлыГлубокого Цикл
ИмяУзла = ТекСтрока.ИмяУзла;
Попытка
ИмяКласса = ТекСтрока.ИмяКласса;
Исключение
ИмяКласса = "";
КонецПопытки;
ТекстовоеСодержимое = СокрЛП(ТекСтрока.ТекстовоеСодержимое);
Если ИмяУзла = "#text" Тогда
Если НЕ ТекСтрока.ПредыдущийСоседний = Неопределено Тогда
Если ТекСтрока.ПредыдущийСоседний.ИмяУзла = "br" Тогда
СодержаниеПоста = СодержаниеПоста + "<p>";
КонецЕсли;
КонецЕсли;
Если ЗначениеЗаполнено(ТекстовоеСодержимое) Тогда
СодержаниеПоста = СодержаниеПоста + ТекстовоеСодержимое;
КонецЕсли;
ИначеЕсли ИмяУзла = "b" Тогда
СодержаниеПоста = СодержаниеПоста + "<span style=""font-weight: bold;""> " + ТекстовоеСодержимое + " </span>";
ИначеЕсли ИмяУзла = "i" Тогда
СодержаниеПоста = СодержаниеПоста + "<span style=""font-style: italic;""> " + ТекстовоеСодержимое + " </span>";
ИначеЕсли ИмяУзла = "img" Тогда
Если СтрНайти(ТекСтрока.Источник, ".gif") = 0 Тогда
Бейс64Картинки = ПолучитьБейс64Картинки(ТекСтрока.Источник);
СодержаниеПоста = СодержаниеПоста + "<p>" + Бейс64Картинки + "</p>";
КонецЕсли;
ИначеЕсли ИмяУзла = "br" Тогда
Если НЕ ТекСтрока.СледующийСоседний = Неопределено Тогда
Если ТекСтрока.СледующийСоседний.ИмяУзла = "br" Тогда
// перенос строки
СодержаниеПоста = СодержаниеПоста + "<p><br></p>";
Иначе
СодержаниеПоста = СодержаниеПоста + "</p>";
КонецЕсли;
Иначе
СодержаниеПоста = СодержаниеПоста + "</p>";
КонецЕсли;
ИначеЕсли ИмяУзла = "span" Тогда
Если ИмяКласса = "offtop" Тогда
СодержаниеПоста = СодержаниеПоста + "<p><span style=""color: #808080;font-style: italic;"">" + ТекстовоеСодержимое + "</span></p>";
ИначеЕсли ИмяКласса = "more_hide" Тогда
СодержаниеПоста = СодержаниеПоста + "<p>" + ТекстовоеСодержимое + "</p>";
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
НоваяСтрока.СодержаниеПоста = СодержаниеПоста;
НоваяСтрока = Неопределено;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Полученный текст можно отредактировать
При выводе содержания постов в табличный документ (для последующего сохранения в pdf, чтобы отправить в типографию), пришлось подгонять текст, чтобы не было разрывов в постах и в картинках. Ну это просто ) Немного сложнее было с форматированием, но решилось заданием цвета и шрифта ячеек через свойства параграфа форматированного документа.
В итоге имеется конфигурация с парсером дневников сайта diary.ru с сохранением каждого поста в документ с картинками, с возможностью редактирования и выводом постов в табличный документ.
Использовалась платформа 8.3.20.1674.