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

08.02.13

Разработка - Универсальные функции

Парсинг 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(элдок,мТаблиц);

 КонецЕсли;

 КонецЕсли;

 КонецЦикла;

 КонецЕсли;

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

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

 

Вступайте в нашу телеграмм-группу Инфостарт

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3354    Abysswalker    8    

44

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

Порой необходимо временно отключить расширение 1С, не удаляя его, чтобы не потерять данные. Но в этом случае при каждом запуске всем будет лезть уведомление о неактивном расширении, хотя очевидно, это техническая информация, которой не стоит лишний раз пугать пользователей.

14.05.2025    6237    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    48447    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7272    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    60524    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    9049    ke.92@mail.ru    17    

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

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

Если элдок.ДочерниеУзлы.Количество()=1 Тогда
рЭлемент=элдок.ДочерниеУзлы.Элемент(0);
romankoav; +1 Ответить
17. romankoav 4 18.05.20 10:34 Сейчас в теме
(16)Сделал сравнение на ДочерниеУзлы.Количество() > 0 и 1 цикл выкинул. Работает. Также в 1 ячейке вместо текста была ссылка, сделал сравнение на другой тип и из ссылки забирал текстовое представление.
18. sergey1407_1 3 27.10.22 13:39 Сейчас в теме
Добрый день.
Немного поменял функцию.
Функция ПолучитьТаблицыЗначенийИзHTML(рИсходныйДокумент)
	
	мТаблицИтого=Новый Массив;
	
	рКодировка="UTF-8";
	
	чтен=Новый ЧтениеHTML;
	
	Если ТипЗнч(рИсходныйДокумент)=Тип("Строка") Тогда
		
		// считаем, что передали строку HTML-кода (фрагмент)
		
		чтен.УстановитьСтроку(рИсходныйДокумент,рКодировка);
		
	ИначеЕсли ТипЗнч(рИсходныйДокумент)=Тип("Файл") Тогда
		
		// считаем, что передали файл, проверенно существующий и читающийся оттуда, где он есть
		
		чтен.ОткрытьФайл(рИсходныйДокумент.ПолноеИмя,рКодировка);
		
	Иначе
		
		Сообщить("Передан неверный аргумент: "+СокрЛП(рИсходныйДокумент)+", недопустимый тип!",СтатусСообщения.Важное);
		
		Возврат мТаблицИтого;
		
	КонецЕсли;
	
	пострДОМ=Новый ПостроительDOM;
	
	гдок=пострДОМ.Прочитать(чтен);
	
	ТаблицыДокумента = гдок.ПолучитьЭлементыПоИмени("table");
	
	Для каждого ТаблицаДокумента Из ТаблицыДокумента Цикл
		
		ВремТаблица = Новый ТаблицаЗначений;
		
		ПерваяСтрока 		= ТаблицаДокумента.ПервыйДочерний;
		КолонкиПервойСтроки = ПерваяСтрока.ПолучитьЭлементыПоИмени("td");
		
		КоличествоКолонок = КолонкиПервойСтроки.Количество();
		
		Для НомКолонки = 1 по КоличествоКолонок Цикл
			ИмяКолонки = СтрШаблон("К%1",Формат(НомКолонки,"ЧГ=0"));
			ВремТаблица.Колонки.Добавить(ИмяКолонки);
		КонецЦикла;
		
		СтрокиТаблицы = ТаблицаДокумента.ПолучитьЭлементыПоИмени("tr");
		Для каждого СтрокаТаблицы из СтрокиТаблицы Цикл                  
			НоваяСтрока = ВремТаблица.добавить();
			КолонкиСтроки = СтрокаТаблицы.ПолучитьЭлементыПоИмени("td");
			КоличествоКолонокВСтроке = 	КолонкиСтроки.Количество(); 
			
			МинимальноеКоличествоКолонокВСтроке = Мин(КоличествоКолонокВСтроке,КоличествоКолонок);
			Для НомКолонки = 0 по  МинимальноеКоличествоКолонокВСтроке - 1 Цикл
				ИмяКолонки = СтрШаблон("К%1",Формат(НомКолонки+1,"ЧГ=0"));
				
				Ячейка = КолонкиСтроки[НомКолонки];
				ТекстЯчейки = Ячейка.ТекстовоеСодержимое;
				НоваяСтрока[ИмяКолонки] = ТекстЯчейки;
			КонецЦикла;  
		КонецЦикла;
		
		мТаблицИтого.добавить(ВремТаблица);			
		
	КонецЦикла;
	
	Возврат мТаблицИтого;
	
КонецФункции
Показать
suepifanov; +1 Ответить
19. ddk77 17.03.24 23:58 Сейчас в теме
У меня в итоге так не заработало: для моего файла выдавалась куча полупустых таблиц непонятного содержания. Переделал функцию и получилось почти идеально (не заморачивался на "горизонтальные" таблицы (где заголовки в строке) и объединенные ячейки).
Функция ПолучитьТаблицыЗначенийИзHTML(ИмяФайла)
	ЧтениеHTML     = Новый ЧтениеHTML;
	ЧтениеHTML.ОткрытьФайл(ИмяФайла, "UTF-8");
	ПостроительDOM = Новый ПостроительDOM;
	ДокументHTML   = ПостроительDOM.Прочитать(ЧтениеHTML);
	
	// Находим все таблицы в исходном файле
	Таблицы    =  ДокументHTML.ПолучитьЭлементыПоИмени("table");
	М = Новый Массив;
	Для Каждого Таблица Из Таблицы Цикл
		// Ищем заголовки
		Заголовки = Таблица.ПолучитьЭлементыПоИмени("th"); 
		Если Заголовки.Количество() > 0 Тогда
			ТЗ = Новый ТаблицаЗначений;      
			к = 0;
			Для каждого С Из Заголовки Цикл  
				к = к + 1;   
				// Создаем колонки таблицы значений с заголовками из файла, для последующего упрощения разбора
				ТЗ.Колонки.Добавить("Кол" + к, , СокрЛП(С.ТекстовоеСодержимое));
			КонецЦикла; 
			// Ищем строки таблиц
			Строки = Таблица.ПолучитьЭлементыПоИмени("tr");
			Для каждого Стр Из Строки Цикл
				С = ТЗ.Добавить();
				// Ищем колонки для каждой строки
				Колонки = Стр.ПолучитьЭлементыПоИмени("td"); 
				к = 0;
				Для каждого Кол Из Колонки Цикл  
					С[к] = "";
					Для каждого Эл Из Кол.ДочерниеУзлы Цикл
						Если Эл.ИмяУзла = "br" Тогда // в строке заменяем перенос строки на "; " (просто для удобства последующего разбора)
							С[к] = С[к] + "; ";
						ИначеЕсли Эл.ИмяУзла = "#text" Тогда
							С[к] = С[к] + СокрЛП(Эл.ЗначениеУзла);
						КонецЕсли;
					КонецЦикла;
					к = к + 1;
				КонецЦикла;
			КонецЦикла;
			М.Добавить(ТЗ);
		КонецЕсли;
	КонецЦикла;  
	Возврат М;
КонецФункции

Показать
Для отправки сообщения требуется регистрация/авторизация