gifts2017

Из html-таблиц в таблицы значений, функция

Опубликовал Яков Коган (Yashazz) в раздел Программирование - Практика программирования

Парсинг HTML-таблиц, занесение результатов в таблицы значений. Обработка вложенных друг в друга html-таблиц. Обработка объединённых ячеек. Одна функция - и готово.

Делал тут чтение с одного сайта, да и написал функцию... Дай, думаю, выложу - авось прямо в таком виде кому пригодится. Что умеет: умеет из файла или из html-фрагмента читать все находящиеся в нём таблицы, включая вложенные друг в друга. Итоговый результат - массив из таблиц значений. В ячейки таблиц пишется либо простое значение (обычно строковое), либо вложенная таблица значений, либо массив значений (если ещё были узлы, в т.ч. вложенные таблицы). Колонки - произвольного типа, их именование примитивное, "к1", "к2" и т.д. На правильность чтения вертикально объединённых ячеек (rowSpan) не рассчитана.

Прошу пардону, но раскраска исходников лежит вне досягаемости; потом разрисую, а копипастить уже и так можно.


Функция ПолучитьТаблицыЗначенийИзHTML(рИсходныйДокумент,мТаблиц=Неопределено)

 Если
мТаблиц=Неопределено Тогда // первый запуск, читаем сам документ

 
мТаблицИтого=Новый Массив;

 
рКодировка="UTF-8";

 
чтен=Новый ЧтениеHTML;

 Если
ТипЗнч(рИсходныйДокумент)=Тип("Строка") Тогда

 
// считаем, что передали строку HTML-кода (фрагмент)

 
чтен.УстановитьСтроку(рИсходныйДокумент,рКодировка);

 ИначеЕсли
ТипЗнч(рИсходныйДокумент)=Тип("Файл") Тогда

 
// считаем, что передали файл, проверенно существующий и читающийся оттуда, где он есть

 
чтен.ОткрытьФайл(рИсходныйДокумент.ПолноеИмя,рКодировка);

 Иначе

 
Сообщить("Передан неверный аргумент: "+СокрЛП(рИсходныйДокумент)+", недопустимый тип!",СтатусСообщения.Важное);

 Возврат
мТаблицИтого;

 КонецЕсли;

 
пострДОМ=Новый ПостроительDOM;

 
гдок=пострДОМ.Прочитать(чтен);

 
// запускаем уже рекурсивное чтение

 
ПолучитьТаблицыЗначенийИзHTML(гдок.ЭлементДокумента,мТаблицИтого);

 
// возвращаемся с верхнего уровня и вообще выходим

 
Возврат мТаблицИтого;

 Иначе

 
// рекурсивный запуск, уже читается ДокументHTML

 
Для каждого элдок Из рИсходныйДокумент.ДочерниеУзлы Цикл

 Если
ТипЗнч(элдок)=Тип("ЭлементТаблицаHTML") Тогда // таблица, переходим непосредственно к её обработке

 //===========================================================================================

 
Если элдок.ДочерниеУзлы.Количество()=1 Тогда

 
рЭлемент=элдок.ДочерниеУзлы.Элемент(0);

 Если
ТипЗнч(рЭлемент)=Тип("ЭлементHTML") Тогда

 Если
рЭлемент.ЕстьДочерниеУзлы() Тогда

 
// колонки добавляются по факту и по порядку

 
тзнч=Новый ТаблицаЗначений;

 Для каждого
рСтрока Из рЭлемент.ДочерниеУзлы Цикл

 
ОбработкаПрерыванияПользователя();

 
мЗначенийКолонок=Новый Массив;

 Для каждого
рЯчейка Из рСтрока.ДочерниеУзлы Цикл

 
ОбработкаПрерыванияПользователя();

 Если
ТипЗнч(рЯчейка)<>Тип("ЭлементЯчейкаТаблицыHTML") Тогда Продолжить КонецЕсли;

 
// в ячейке может быть как один элемент (тогда вносится простое значение), так и несколько (тогда их массив)

 
мЗначенийЯчейки=Новый Массив;

 Для каждого
рУзел Из рЯчейка.ДочерниеУзлы ЦИкл

 Если
ТипЗнч(рУзел)=Тип("ТекстDOM") Тогда

 
рЗначение=СокрЛП(рУзел.ТекстовоеСодержимое);

 ИначеЕсли
ТипЗнч(рУзел)=Тип("ЭлементТаблицаHTML") Тогда

 
// идём ещё глубже, обрабатывая таблицу через вызов обработки её родителя

 
мПодтаблиц=Новый Массив;

 
ПолучитьТаблицыЗначенийИзHTML(рУзел.РодительскийУзел,мПодтаблиц);

 Если
мПодтаблиц.Количество()=0 Тогда // ни одной таблицы не было

 
рЗначение=Неопределено;

 ИначеЕсли
мПодтаблиц.Количество()=1 Тогда

 
рЗначение=мПодтаблиц.Получить(0);

 Иначе

 
рЗначение=мПодтаблиц; // прямо как массив и идёт

 
КонецЕсли;

 Иначе

 
// идём ещё глубже в общем виде

 
мПодтаблиц=Новый Массив;

 
ПолучитьТаблицыЗначенийИзHTML(рУзел,мПодтаблиц);

 Если
мПодтаблиц.Количество()=0 Тогда // ни одной таблицы не было

 
рЗначение=Неопределено;

 ИначеЕсли
мПодтаблиц.Количество()=1 Тогда

 
рЗначение=мПодтаблиц.Получить(0);

 Иначе

 
рЗначение=мПодтаблиц; // прямо как массив и идёт

 
КонецЕсли;

 КонецЕсли;

 Если
ТипЗнч(рЗначение)=Тип("Массив") Тогда // перекидываем в итоговый

 
Для каждого знчм Из рЗначение Цикл мЗначенийЯчейки.Добавить(знчм) КонецЦикла;

 Иначе
// просто добавляем

 
мЗначенийЯчейки.Добавить(рЗначение);

 КонецЕсли;

 КонецЦикла;

 
рЗначениеЯчейки=?(мЗначенийЯчейки.Количество()=1,мЗначенийЯчейки[0],мЗначенийЯчейки);

 
// определяем, сколько колонок вмещает текущая ячейка (возможно объединение, тогда будет повтор значения)

 
квокол=?(рЯчейка.ОбъединениеКолонок=0,1,рЯчейка.ОбъединениеКолонок);

 
//Если рЯчейка.Атрибуты.Количество()<>0 Тогда // можно и так, оставил для общего интересу...

 // рАтрОбъКол=рЯчейка.Атрибуты.ПолучитьИменованныйЭлемент("colspan"); // именно в нижнем регистре

 // Если рАтрОбъКол<>Неопределено Тогда квокол=рАтрОбъКол.Значение КонецЕсли;

 //КонецЕсли;

 
Для й=1 По квокол Цикл

 
мЗначенийКолонок.Добавить(рЗначениеЯчейки);

 КонецЦикла;

 КонецЦикла;

 
// теперь массив значений для строки таблицы значений готов

 
стротзнч=тзнч.Добавить();

 Для
ы=0 По мЗначенийКолонок.Количество()-1 Цикл

 Если
ы>тзнч.Колонки.Количество()-1 Тогда // по ситуации добавляем колонку произвольного типа

 
тзнч.Колонки.Добавить("к"+СокрЛП(ы));

 КонецЕсли;

 
стротзнч[ы]=мЗначенийКолонок.Получить(ы);

 КонецЦикла;

 КонецЦикла;

 Иначе

 
тзнч=Неопределено;

 КонецЕсли;
// если есть дочерние/нет дочерних

 
Иначе

 
тзнч=Неопределено;

 КонецЕсли;
// по типу исходного основного элемента таблицы

 
Иначе

 
тзнч=Неопределено;

 КонецЕсли;
// по количеству исходных узлов

 //===========================================================================================

 
Если тзнч<>Неопределено Тогда мТаблиц.Добавить(тзнч) КонецЕсли;

 Иначе

 Если
элдок.ЕстьДочерниеУзлы() Тогда

 
ПолучитьТаблицыЗначенийИзHTML(элдок,мТаблиц);

 КонецЕсли;

 КонецЕсли;

 КонецЦикла;

 КонецЕсли;

 Возврат Неопределено;
// тут результат нас не интересует

КонецФункции

 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Андрей Акулов (DrAku1a) 08.02.13 10:06
(0) оформите пожалуйста код, как полагается.
2. Яков Коган (Yashazz) 08.02.13 11:24
(1) Я ж написал, разукрашки под рукой не случилось. ;) Сегодня оформлю.
3. Игорь Steelvan (Steelvan) 11.02.13 13:39
А есть возможность обратной реализации?
Из Таблицы значений (со вложенными таблицами) в таблицу HTML ?
Пригодилось бы.
4. Яков Коган (Yashazz) 11.02.13 14:04
(3) Уж подумываю сделать, есть такая задача.
5. Игорь Steelvan (Steelvan) 11.02.13 17:07
6. vladal (Vladal) 13.02.13 16:07
Яростно плюсую за то, что описал в статье примеры кода.
8. Дмитрий Г (Дмитрий74Чел) 19.10.13 17:36
у меня не отрабатывает: в моем файле таблица имеет более 1 строки, а у Вас стоит
Если элдок.ДочерниеУзлы.Количество()=1 Тогда

накосячил с копипастом )))
9. Яков Коган (Yashazz) 19.10.13 22:13
(8) Фух, я уж напрягся. Если что, пишите.
10. Виталий Гуляев (vital1c) 29.01.14 15:22
Если таблица с заголовком
<thead>
, то не обрабатывает такие таблицы корректно, а ячейки такого заголовка имеют тип как ЭлементHTML
11. Андрей Гуща (amatoravg) 23.10.14 08:57
не работает. в документе у меня одна таблица. возвращает массив с кучей пустых строк.... :(
12. Яков Коган (Yashazz) 23.10.14 19:37
(11) Пожалуйста, киньте мне на yashazz пёсик mail.ру ваш исходный html, посмотрю. Очень интересно, что там такое попалось.
13. Александр (Иной) 23.12.14 01:09
Заранее спасибо =). Завтра буду юзать
14. andy3626603 (andy3626603) 29.12.14 17:04
У меня то же самое что и 11
15. Яков Коган (Yashazz) 01.01.15 14:59
(14) andy3626603, жду ваш html, посмотрим.
16. L M (lm-alex) 20.06.16 20:31
Не отрабаытвает если ДочерниеУзлы.Количество() > 1

Если элдок.ДочерниеУзлы.Количество()=1 Тогда
рЭлемент=элдок.ДочерниеУзлы.Элемент(0);
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа